* [dpdk-dev] [RFC PATCH 0/2] dynamic memzones
@ 2015-05-08 16:37 Sergio Gonzalez Monroy
2015-05-08 16:37 ` [dpdk-dev] [RFC PATCH 1/2] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (3 more replies)
0 siblings, 4 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-05-08 16:37 UTC (permalink / raw)
To: dev
Please NOTE that this series is meant to illustrate an idea/approach and start
discussion on the topic.
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/delete). This affects mempools and other memzone based objects.
>From my point of view, implementing unreserve functionality for memzones would
look like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc (there would be some ABI changes, see below).
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
An alternative would be to move malloc internals (malloc_heap, malloc_elem)
to the eal, but keeping the malloc library as is, where malloc is based on
memzones. This way we could avoid ABI changes while keeping the existing
circular dependency between malloc and eal.
TODOs:
- Implement memzone_unreserve, simply call rte_malloc_free.
- Implement mempool_delete, simply call rte_memzone_unreserve.
- Init heaps with all available memsegs at once.
- Review symbols in version map.
ABI changes:
- Removed support for rte_memzone_reserve_xxxx with len=0 (not needed?).
- Removed librte_malloc as single library (linker script as work around?).
IDEAS FOR FUTURE WORK:
- More control over requested memory, ie. shared/private, phys_contig, etc.
One of the goals would be trying to reduce the need of physically contiguous
memory when not required.
- Attach/unattach hugepages at runtime (faster VM migration).
- Improve malloc algorithm? ie. jemalloc (or any other).
Any comments/toughts and/or different approaches are welcome.
Sergio Gonzalez Monroy (2):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
lib/Makefile | 1 -
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 18 ++
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 233 ++--------------
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 4 +-
lib/librte_eal/common/include/rte_memory.h | 1 +
lib/librte_eal/common/malloc_elem.c | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 287 ++++++++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 259 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 18 ++
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 52 ----
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 209 ---------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 260 ------------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 19 --
lib/librte_mempool/Makefile | 2 -
lib/librte_pmd_af_packet/Makefile | 1 -
lib/librte_pmd_bond/Makefile | 1 -
lib/librte_pmd_e1000/Makefile | 2 +-
lib/librte_pmd_enic/Makefile | 2 +-
lib/librte_pmd_fm10k/Makefile | 2 +-
lib/librte_pmd_i40e/Makefile | 2 +-
lib/librte_pmd_ixgbe/Makefile | 2 +-
lib/librte_pmd_mlx4/Makefile | 1 -
lib/librte_pmd_null/Makefile | 1 -
lib/librte_pmd_pcap/Makefile | 1 -
lib/librte_pmd_virtio/Makefile | 2 +-
lib/librte_pmd_vmxnet3/Makefile | 2 +-
lib/librte_pmd_xenvirt/Makefile | 2 +-
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
45 files changed, 1571 insertions(+), 1719 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/Makefile
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.c
delete mode 100644 lib/librte_malloc/rte_malloc.h
delete mode 100644 lib/librte_malloc/rte_malloc_version.map
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [RFC PATCH 1/2] eal: move librte_malloc to eal/common
2015-05-08 16:37 [dpdk-dev] [RFC PATCH 0/2] dynamic memzones Sergio Gonzalez Monroy
@ 2015-05-08 16:37 ` Sergio Gonzalez Monroy
2015-05-08 16:37 ` [dpdk-dev] [RFC PATCH 2/2] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (2 subsequent siblings)
3 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-05-08 16:37 UTC (permalink / raw)
To: dev
This patch moves the malloc library inside the eal.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing unreserve/free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
lib/Makefile | 1 -
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 18 ++
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 209 +++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 18 ++
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 52 ----
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 209 ---------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 260 ------------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 19 --
lib/librte_mempool/Makefile | 2 -
lib/librte_pmd_af_packet/Makefile | 1 -
lib/librte_pmd_bond/Makefile | 1 -
lib/librte_pmd_e1000/Makefile | 2 +-
lib/librte_pmd_enic/Makefile | 2 +-
lib/librte_pmd_fm10k/Makefile | 2 +-
lib/librte_pmd_i40e/Makefile | 2 +-
lib/librte_pmd_ixgbe/Makefile | 2 +-
lib/librte_pmd_mlx4/Makefile | 1 -
lib/librte_pmd_null/Makefile | 1 -
lib/librte_pmd_pcap/Makefile | 1 -
lib/librte_pmd_virtio/Makefile | 2 +-
lib/librte_pmd_vmxnet3/Makefile | 2 +-
lib/librte_pmd_xenvirt/Makefile | 2 +-
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
42 files changed, 1450 insertions(+), 1501 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/Makefile
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.c
delete mode 100644 lib/librte_malloc/rte_malloc.h
delete mode 100644 lib/librte_malloc/rte_malloc_version.map
diff --git a/config/common_bsdapp b/config/common_bsdapp
index c2374c0..8f74e7b 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -97,6 +97,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -295,13 +297,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 0078dc9..78ce1e7 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -100,6 +100,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -302,13 +304,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/lib/Makefile b/lib/Makefile
index d94355d..325e80c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,6 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index 2357cfa..7c2c30d 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_pmd_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_pmd_pcap
@@ -78,6 +77,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 67b6a6c..3f4f521 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -92,3 +92,21 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_calloc;
+ rte_calloc_socket;
+ rte_free;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
+ rte_realloc;
+ rte_zmalloc;
+ rte_zmalloc_socket;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 3ea3bbf..ffe94e4 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..defb903
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,209 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
+
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 01f7b70..cf4d238 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/lib/librte_pmd_ring
@@ -90,6 +89,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..01b45a7 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -98,3 +98,21 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_calloc;
+ rte_calloc_socket;
+ rte_free;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
+ rte_realloc;
+ rte_zmalloc;
+ rte_zmalloc_socket;
+} DPDK_2.0;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 3696cb1..57454e6 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -52,6 +52,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_jhash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
deleted file mode 100644
index 947e41c..0000000
--- a/lib/librte_malloc/Makefile
+++ /dev/null
@@ -1,52 +0,0 @@
-# BSD LICENSE
-#
-# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in
-# the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Intel Corporation nor the names of its
-# contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-# library name
-LIB = librte_malloc.a
-
-LIBABIVER := 1
-
-CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
-
-EXPORT_MAP := rte_malloc_version.map
-
-# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
-
-# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
-
-include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index defb903..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
-
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
deleted file mode 100644
index c313a57..0000000
--- a/lib/librte_malloc/rte_malloc.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
deleted file mode 100644
index af6ae9b..0000000
--- a/lib/librte_malloc/rte_malloc_version.map
+++ /dev/null
@@ -1,19 +0,0 @@
-DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
- local: *;
-};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_af_packet/Makefile b/lib/librte_pmd_af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/lib/librte_pmd_af_packet/Makefile
+++ b/lib/librte_pmd_af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile
index 83ccce3..dee0875 100644
--- a/lib/librte_pmd_bond/Makefile
+++ b/lib/librte_pmd_bond/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/lib/librte_pmd_e1000/Makefile b/lib/librte_pmd_e1000/Makefile
index 8c8fed8..927f5d1 100644
--- a/lib/librte_pmd_e1000/Makefile
+++ b/lib/librte_pmd_e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_enic/Makefile b/lib/librte_pmd_enic/Makefile
index 251a898..ebe6083 100644
--- a/lib/librte_pmd_enic/Makefile
+++ b/lib/librte_pmd_enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += vnic/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_fm10k/Makefile b/lib/librte_pmd_fm10k/Makefile
index 7516d37..2804731 100644
--- a/lib/librte_pmd_fm10k/Makefile
+++ b/lib/librte_pmd_fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_i40e/Makefile b/lib/librte_pmd_i40e/Makefile
index 64bab16..fc536e9 100644
--- a/lib/librte_pmd_i40e/Makefile
+++ b/lib/librte_pmd_i40e/Makefile
@@ -100,6 +100,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_ixgbe/Makefile b/lib/librte_pmd_ixgbe/Makefile
index fbf6966..1555d7a 100644
--- a/lib/librte_pmd_ixgbe/Makefile
+++ b/lib/librte_pmd_ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/lib/librte_pmd_mlx4/Makefile b/lib/librte_pmd_mlx4/Makefile
index 97b364a..d45aa9d 100644
--- a/lib/librte_pmd_mlx4/Makefile
+++ b/lib/librte_pmd_mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/lib/librte_pmd_null/Makefile b/lib/librte_pmd_null/Makefile
index 6472015..96ba01c 100644
--- a/lib/librte_pmd_null/Makefile
+++ b/lib/librte_pmd_null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_pcap/Makefile b/lib/librte_pmd_pcap/Makefile
index 0775dbc..48be913 100644
--- a/lib/librte_pmd_pcap/Makefile
+++ b/lib/librte_pmd_pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_virtio/Makefile b/lib/librte_pmd_virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/lib/librte_pmd_virtio/Makefile
+++ b/lib/librte_pmd_virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_vmxnet3/Makefile b/lib/librte_pmd_vmxnet3/Makefile
index fc616c4..72c74e2 100644
--- a/lib/librte_pmd_vmxnet3/Makefile
+++ b/lib/librte_pmd_vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_pmd_xenvirt/Makefile b/lib/librte_pmd_xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/lib/librte_pmd_xenvirt/Makefile
+++ b/lib/librte_pmd_xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [RFC PATCH 2/2] eal: memzone allocated by malloc
2015-05-08 16:37 [dpdk-dev] [RFC PATCH 0/2] dynamic memzones Sergio Gonzalez Monroy
2015-05-08 16:37 ` [dpdk-dev] [RFC PATCH 1/2] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-05-08 16:37 ` Sergio Gonzalez Monroy
2015-05-12 16:30 ` [dpdk-dev] [RFC PATCH 0/2] dynamic memzones Olivier MATZ
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
3 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-05-08 16:37 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically contiguous
hugepages, memzone are slices of memsegs and malloc further slices memzones
into smaller memory chunks.
This patch modifies malloc so it slices/partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memoy allocation while
maintaining its ABI. The only exception is the reserving a memzone with
len=0 is not supported anymore.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 233 ++----------------------
lib/librte_eal/common/include/rte_malloc_heap.h | 4 +-
lib/librte_eal/common/include/rte_memory.h | 1 +
lib/librte_eal/common/malloc_elem.c | 60 ++++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 188 +++++++++++++------
lib/librte_eal/common/malloc_heap.h | 4 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
8 files changed, 207 insertions(+), 304 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 888f9e5..3dc8133 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,11 +50,10 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
@@ -88,53 +87,12 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
len, socket_id, flags, RTE_CACHE_LINE_SIZE);
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
-{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
-
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
-
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
-
- while (addr_offset + len < ms->len) {
-
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
-
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
- }
-
- return (addr_offset);
-}
-
static const struct rte_memzone *
memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -166,10 +124,10 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
- /* align length on cache boundary. Check for overflow before doing so */
- if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
- rte_errno = EINVAL; /* requested size too big */
+ /* align length on cache boundary. Check for overflow before doing so
+ * FIXME need to update API doc regarding len value*/
+ if ((len > SIZE_MAX - RTE_CACHE_LINE_MASK) || (len == 0)){
+ rte_errno = EINVAL;
return NULL;
}
@@ -186,123 +144,29 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
-
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- /* check flags for hugepage sizes */
- if ((flags & RTE_MEMZONE_2MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_1G)
- continue;
- if ((flags & RTE_MEMZONE_1GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_2M)
- continue;
- if ((flags & RTE_MEMZONE_16MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16G)
- continue;
- if ((flags & RTE_MEMZONE_16GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16M)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /* no segment found */
- if (memseg_idx == -1) {
- /*
- * If RTE_MEMZONE_SIZE_HINT_ONLY flag is specified,
- * try allocating again without the size parameter otherwise -fail.
- */
- if ((flags & RTE_MEMZONE_SIZE_HINT_ONLY) &&
- ((flags & RTE_MEMZONE_1GB) || (flags & RTE_MEMZONE_2MB)
- || (flags & RTE_MEMZONE_16MB) || (flags & RTE_MEMZONE_16GB)))
- return memzone_reserve_aligned_thread_unsafe(name,
- len, socket_id, 0, align, bound);
+ /* get socket heap */
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -419,45 +283,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -465,14 +290,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -485,26 +306,6 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..5333348 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 10
/**
* Structure to hold malloc heap
@@ -48,7 +48,7 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
+ unsigned ms_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/include/rte_memory.h b/lib/librte_eal/common/include/rte_memory.h
index 7f8103f..ab13d04 100644
--- a/lib/librte_eal/common/include/rte_memory.h
+++ b/lib/librte_eal/common/include/rte_memory.h
@@ -100,6 +100,7 @@ struct rte_memseg {
/**< store segment MFNs */
uint64_t mfn[DOM0_NUM_MEMBLOCK];
#endif
+ uint8_t used; /**< already used by a heap */
} __attribute__((__packed__));
/**
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..5e95abb 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != (end_pt & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if ((end_pt & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -118,7 +130,7 @@ split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
const unsigned new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -190,12 +202,25 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD * 2 + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +233,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +242,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index defb903..b79e0e9 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,69 +53,136 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+static unsigned
+check_hugepage_sz(unsigned flags, size_t hugepage_sz)
+{
+ unsigned ret = 1;
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+ if ((flags & RTE_MEMZONE_2MB) && hugepage_sz == RTE_PGSIZE_1G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_1GB) && hugepage_sz == RTE_PGSIZE_2M)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16MB) && hugepage_sz == RTE_PGSIZE_16G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16GB) && hugepage_sz == RTE_PGSIZE_16M)
+ ret = 0;
+
+ return ret;
+}
+
+static struct rte_memseg*
+find_suitable_memseg(int socket_id, size_t size, unsigned flags,
+ size_t align, size_t bound)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ struct rte_memseg *ms = rte_eal_get_configuration()->mem_config->memseg;
+ uintptr_t data_end, data_start;
+ size_t bmask = ~(bound - 1);
+ unsigned i;
+ int ms_idx = -1, alt_ms_idx = -1;
+ size_t ms_len = 0, alt_ms_len = 0;
+ size_t min_size;
+
+ min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+
+ for (i = 0; i < RTE_MAX_MEMSEG; i++) {
+ /* last segment */
+ if (ms[i].addr == NULL)
+ break;
+
+ /* in use */
+ if (ms[i].used)
+ continue;
+
+ /* bad socket ID */
+ if (socket_id != SOCKET_ID_ANY && ms[i].socket_id != SOCKET_ID_ANY &&
+ socket_id != ms[i].socket_id)
+ continue;
+
+ /* check len */
+ if (min_size > ms[i].len)
+ continue;
+
+ /* check boundary */
+ data_end = (uintptr_t)ms[i].addr + ms[i].len -
+ MALLOC_ELEM_OVERHEAD - MALLOC_ELEM_TRAILER_LEN ;
+ data_end = RTE_ALIGN_FLOOR(data_end, RTE_CACHE_LINE_SIZE);
+ data_start = RTE_ALIGN_FLOOR((data_end - size), align);
+ if ((data_end & bmask) != (data_start & bmask)) {
+ /* check we have enough space before boudnary */
+ data_end = RTE_ALIGN_FLOOR(data_end, bound);
+ data_start = RTE_ALIGN_FLOOR((data_end - size), align);
+ if (((data_end & bmask) != (data_start & bmask)) ||
+ ((uintptr_t)ms[i].addr > (data_start - MALLOC_ELEM_HEADER_LEN)))
+ continue;
+ }
+
+ /* at this point, we have a memseg */
+
+ /* keep best memseg found */
+ if ((alt_ms_idx == -1) ||
+ (ms[i].len < alt_ms_len)) {
+ alt_ms_idx = i;
+ alt_ms_len = ms[i].len;
+ }
+
+ /* check flags for hugepage sizes */
+ if (!check_hugepage_sz(flags, ms[i].hugepage_sz))
+ continue;
+
+ /* keep best memseg found with requested hugepage size */
+ if ((ms_idx == -1) ||
+ (ms[i].len < ms_len)) {
+ ms_idx = i;
+ ms_len = ms[i].len;
+ }
+ }
+
+ if ((ms_idx == -1) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ ms_idx = alt_ms_idx;
+
+ if (ms_idx == -1)
+ return NULL;
+
+ return &ms[ms_idx];
}
+/* This function expects correct values:
+ * - size: >= RTE_CACHE_LINE_SIZE
+ * - align: power_of_two && >= RTE_CACHE_LINE_SIZE
+ * - bound: power_of_two && >= size
+ */
/*
+ * find a suitable memory segment available to expand the heap
* reserve an extra memory zone and make it available for use by a particular
* heap. This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+malloc_heap_add_memseg(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
+ struct rte_memseg *ms = NULL;
struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
+ unsigned socket_id = heap - mcfg->malloc_heaps;
+
+ ms = find_suitable_memseg(socket_id, size, flags, align, bound);
+ if (ms == NULL)
return -1;
+ ms->used = 1;
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ heap->total_size += ms->len - MALLOC_ELEM_OVERHEAD;
return 0;
}
@@ -126,10 +192,11 @@ malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
idx < RTE_HEAP_NUM_FREELISTS; idx++)
@@ -137,40 +204,51 @@ find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
for (elem = LIST_FIRST(&heap->free_head[idx]);
!!elem; elem = LIST_NEXT(elem, free_list))
{
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ else
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ struct malloc_elem *elem = find_suitable_element(heap, size, flags,
+ align, bound);
if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
+ if ((malloc_heap_add_memseg(heap, size, flags, align, bound)) == 0)
+ elem = find_suitable_element(heap, size, flags, align, bound);
}
if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..4ba3353 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,8 +53,8 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [RFC PATCH 0/2] dynamic memzones
2015-05-08 16:37 [dpdk-dev] [RFC PATCH 0/2] dynamic memzones Sergio Gonzalez Monroy
2015-05-08 16:37 ` [dpdk-dev] [RFC PATCH 1/2] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-05-08 16:37 ` [dpdk-dev] [RFC PATCH 2/2] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-05-12 16:30 ` Olivier MATZ
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
3 siblings, 0 replies; 108+ messages in thread
From: Olivier MATZ @ 2015-05-12 16:30 UTC (permalink / raw)
To: Sergio Gonzalez Monroy, dev
Hi Sergio,
On 05/08/2015 06:37 PM, Sergio Gonzalez Monroy wrote:
> Please NOTE that this series is meant to illustrate an idea/approach and start
> discussion on the topic.
>
> Current implemetation allows reserving/creating memzones but not the opposite
> (unreserve/delete). This affects mempools and other memzone based objects.
>
> From my point of view, implementing unreserve functionality for memzones would
> look like malloc over memsegs.
> Thus, this approach moves malloc inside eal (which in turn removes a circular
> dependency), where malloc heaps are composed of memsegs.
> We keep both malloc and memzone APIs as they are, but memzones allocate its
> memory by calling malloc_heap_alloc (there would be some ABI changes, see below).
> Some extra functionality is required in malloc to allow for boundary constrained
> memory requests.
> In summary, currently malloc is based on memzones, and with this approach
> memzones are based on malloc.
>
> An alternative would be to move malloc internals (malloc_heap, malloc_elem)
> to the eal, but keeping the malloc library as is, where malloc is based on
> memzones. This way we could avoid ABI changes while keeping the existing
> circular dependency between malloc and eal.
>
> TODOs:
> - Implement memzone_unreserve, simply call rte_malloc_free.
> - Implement mempool_delete, simply call rte_memzone_unreserve.
> - Init heaps with all available memsegs at once.
> - Review symbols in version map.
>
> ABI changes:
> - Removed support for rte_memzone_reserve_xxxx with len=0 (not needed?).
> - Removed librte_malloc as single library (linker script as work around?).
>
> IDEAS FOR FUTURE WORK:
> - More control over requested memory, ie. shared/private, phys_contig, etc.
> One of the goals would be trying to reduce the need of physically contiguous
> memory when not required.
> - Attach/unattach hugepages at runtime (faster VM migration).
> - Improve malloc algorithm? ie. jemalloc (or any other).
>
>
> Any comments/toughts and/or different approaches are welcome.
I like the idea and I don't see any issue on the principle. It
will clearly help to have dynamic pools or rings.
(I didn't dive in the second patch very deep, it's just a
high-level thought).
Regards,
Olivier
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v2 0/7] dynamic memzone
2015-05-08 16:37 [dpdk-dev] [RFC PATCH 0/2] dynamic memzones Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-05-12 16:30 ` [dpdk-dev] [RFC PATCH 0/2] dynamic memzones Olivier MATZ
@ 2015-06-06 10:32 ` Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 1/7] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (10 more replies)
3 siblings, 11 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-06 10:32 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
TODOs:
- checkpatch: current malloc code gives plenty of errors
Sergio Gonzalez Monroy (7):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove setup of free_memseg in ivshmem
eal: new rte_memzone_free
app/test: update unit test with rte_memzone_free
app/test/test_malloc.c | 86 -----
app/test/test_memzone.c | 439 +++-------------------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 1 -
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 323 ++++++----------
lib/librte_eal/common/include/rte_eal_memconfig.h | 4 +-
lib/librte_eal/common/include/rte_malloc.h | 342 +++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memory.h | 1 +
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 +++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 ++++++++++
lib/librte_eal/common/malloc_heap.c | 207 ++++++++++
lib/librte_eal/common/malloc_heap.h | 70 ++++
lib/librte_eal/common/rte_malloc.c | 259 +++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 37 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 52 ---
lib/librte_malloc/malloc_elem.c | 320 ----------------
lib/librte_malloc/malloc_elem.h | 190 ----------
lib/librte_malloc/malloc_heap.c | 209 ----------
lib/librte_malloc/malloc_heap.h | 70 ----
lib/librte_malloc/rte_malloc.c | 260 -------------
lib/librte_malloc/rte_malloc.h | 342 -----------------
lib/librte_malloc/rte_malloc_version.map | 19 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
50 files changed, 1685 insertions(+), 2193 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/Makefile
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.c
delete mode 100644 lib/librte_malloc/rte_malloc.h
delete mode 100644 lib/librte_malloc/rte_malloc_version.map
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v2 1/7] eal: move librte_malloc to eal/common
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
@ 2015-06-06 10:32 ` Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 2/7] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (9 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-06 10:32 UTC (permalink / raw)
To: dev
This patch moves the malloc library inside the eal.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing unreserve/free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 1 -
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 209 +++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 52 ----
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 209 ---------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 260 ------------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 19 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
42 files changed, 1440 insertions(+), 1501 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/Makefile
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.c
delete mode 100644 lib/librte_malloc/rte_malloc.h
delete mode 100644 lib/librte_malloc/rte_malloc_version.map
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 0b169c8..5d3cc39 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -97,6 +97,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -295,13 +297,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 5deb55a..810168f 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -100,6 +100,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -302,13 +304,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 97b364a..d45aa9d 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..9727b83 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,6 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index 3d1d9eb..1a87286 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -78,6 +77,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 67b6a6c..0401be2 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -47,6 +49,7 @@ DPDK_2.0 {
rte_eal_tailq_register;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -62,6 +65,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -75,6 +85,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -88,6 +99,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 3ea3bbf..ffe94e4 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..defb903
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,209 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
+
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 077ea99..9ce50a2 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -90,6 +89,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 3696cb1..57454e6 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -52,6 +52,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_jhash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
deleted file mode 100644
index 947e41c..0000000
--- a/lib/librte_malloc/Makefile
+++ /dev/null
@@ -1,52 +0,0 @@
-# BSD LICENSE
-#
-# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in
-# the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Intel Corporation nor the names of its
-# contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-# library name
-LIB = librte_malloc.a
-
-LIBABIVER := 1
-
-CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
-
-EXPORT_MAP := rte_malloc_version.map
-
-# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
-
-# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
-
-include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index defb903..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
-
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
deleted file mode 100644
index c313a57..0000000
--- a/lib/librte_malloc/rte_malloc.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
deleted file mode 100644
index af6ae9b..0000000
--- a/lib/librte_malloc/rte_malloc_version.map
+++ /dev/null
@@ -1,19 +0,0 @@
-DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
- local: *;
-};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v2 2/7] eal: memzone allocated by malloc
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 1/7] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-06-06 10:32 ` Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 3/7] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (8 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-06 10:32 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically contiguous
hugepages, memzones are slices of memsegs and malloc further slices memzones
into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 273 ++++++----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memory.h | 1 +
lib/librte_eal/common/malloc_elem.c | 68 ++++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 140 ++++++-----
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
9 files changed, 197 insertions(+), 317 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 888f9e5..742f6c9 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,11 +50,10 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
@@ -68,8 +67,9 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr != NULL &&
+ !strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
@@ -88,39 +88,45 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
len, socket_id, flags, RTE_CACHE_LINE_SIZE);
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* Find the heap with the greatest free block size */
+static void
+find_heap_max_free_elem(int *s, size_t *len, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > *len) {
+ *len = stats.greatest_free_size;
+ *s = i;
+ }
+ }
+ *len -= (MALLOC_ELEM_OVERHEAD + align);
+}
- while (addr_offset + len < ms->len) {
+/* Find a heap that can allocate the requested size */
+static void
+find_heap_suitable(int *s, size_t len, unsigned align)
+{
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size >= len + MALLOC_ELEM_OVERHEAD + align) {
+ *s = i;
+ break;
+ }
}
-
- return (addr_offset);
}
static const struct rte_memzone *
@@ -128,13 +134,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -166,7 +166,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -180,129 +179,50 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = 0;
+ }
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- /* check flags for hugepage sizes */
- if ((flags & RTE_MEMZONE_2MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_1G)
- continue;
- if ((flags & RTE_MEMZONE_1GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_2M)
- continue;
- if ((flags & RTE_MEMZONE_16MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16G)
- continue;
- if ((flags & RTE_MEMZONE_16GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16M)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY) {
+ if (requested_len == 0)
+ find_heap_max_free_elem(&socket_id, &requested_len, align);
+ else
+ find_heap_suitable(&socket_id, requested_len, align);
+
+ if (socket_id == SOCKET_ID_ANY) {
+ rte_errno = ENOMEM;
+ return NULL;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
- /*
- * If RTE_MEMZONE_SIZE_HINT_ONLY flag is specified,
- * try allocating again without the size parameter otherwise -fail.
- */
- if ((flags & RTE_MEMZONE_SIZE_HINT_ONLY) &&
- ((flags & RTE_MEMZONE_1GB) || (flags & RTE_MEMZONE_2MB)
- || (flags & RTE_MEMZONE_16MB) || (flags & RTE_MEMZONE_16GB)))
- return memzone_reserve_aligned_thread_unsafe(name,
- len, socket_id, 0, align, bound);
-
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0? elem->size: requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -419,45 +339,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -465,14 +346,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -485,33 +362,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/include/rte_memory.h b/lib/librte_eal/common/include/rte_memory.h
index 7f8103f..675b630 100644
--- a/lib/librte_eal/common/include/rte_memory.h
+++ b/lib/librte_eal/common/include/rte_memory.h
@@ -100,6 +100,7 @@ struct rte_memseg {
/**< store segment MFNs */
uint64_t mfn[DOM0_NUM_MEMBLOCK];
#endif
+ uint8_t used; /**< Used by a heap */
} __attribute__((__packed__));
/**
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index defb903..4a423c1 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,105 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, size_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned ret = 1;
+
+ if ((flags & RTE_MEMZONE_2MB) && hugepage_sz == RTE_PGSIZE_1G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_1GB) && hugepage_sz == RTE_PGSIZE_2M)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16MB) && hugepage_sz == RTE_PGSIZE_16G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16GB) && hugepage_sz == RTE_PGSIZE_16M)
+ ret = 0;
+
+ return ret;
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ else
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -207,3 +188,20 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
return 0;
}
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v2 3/7] app/test: update malloc/memzone unit tests
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 1/7] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 2/7] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-06-06 10:32 ` Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 4/7] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (7 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-06 10:32 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 436 ++++--------------------------------------------
2 files changed, 35 insertions(+), 487 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..c5e4872 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ maxlen = find_max_block_free_size(align);
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
-
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -765,278 +683,6 @@ test_memzone_bounded(void)
return (0);
}
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
return 0;
}
@@ -1125,14 +771,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +787,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v2 4/7] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 3/7] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-06-06 10:32 ` Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 5/7] eal: remove setup of free_memseg in ivshmem Sergio Gonzalez Monroy
` (6 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-06 10:32 UTC (permalink / raw)
To: dev
This ocnfig option is not used anymore and thus remove.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 5d3cc39..cf495ad 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -98,7 +98,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 810168f..5e81614 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -101,7 +101,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v2 5/7] eal: remove setup of free_memseg in ivshmem
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 4/7] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-06-06 10:32 ` Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 6/7] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (5 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-06 10:32 UTC (permalink / raw)
To: dev
Remove code setting up free_memseg as it is not used/relevant anymore.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v2 6/7] eal: new rte_memzone_free
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 5/7] eal: remove setup of free_memseg in ivshmem Sergio Gonzalez Monroy
@ 2015-06-06 10:32 ` Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 7/7] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
` (4 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-06 10:32 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 +++
lib/librte_eal/common/eal_common_memzone.c | 50 +++++++++++++++++++++--
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 28 +++++++++++--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 +++
6 files changed, 95 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 0401be2..7110816 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -105,3 +105,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 742f6c9..0b458ec 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -76,6 +76,23 @@ memzone_lookup_thread_unsafe(const char *name)
return NULL;
}
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++);
+
+ if (i < RTE_MAX_MEMZONE)
+ return &mcfg->memzone[i];
+
+ return NULL;
+}
+
/*
* Return a pointer to a correctly filled memzone descriptor. If the
* allocation cannot be done, return NULL.
@@ -140,7 +157,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -214,7 +231,8 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -290,6 +308,32 @@ rte_memzone_reserve_bounded(const char *name, size_t len,
return mz;
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_read_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ mcfg->memzone[idx].addr = NULL;
+ rte_free(addr);
+
+ rte_rwlock_read_unlock(&mcfg->mlock);
+
+ return ret;
+}
/*
* Lookup for the memzone identified by the given name
@@ -363,7 +407,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..2015074 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index 81b6ad4..3f54bde 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -240,6 +240,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - sucess
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..db021c7 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -735,6 +735,23 @@ map_all_segments(void)
return 0;
}
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++);
+
+ if (i < RTE_MAX_MEMZONE)
+ return &mcfg->memzone[i];
+
+ return NULL;
+}
+
/* this happens at a later stage, after general EAL memory initialization */
int
rte_eal_ivshmem_obj_init(void)
@@ -768,12 +785,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = get_next_free_memzone();
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,15 +813,18 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
mz = &mcfg->memzone[i];
+ if (mz->addr == NULL)
+ continue;
+
/* check if memzone has a ring prefix */
if (strncmp(mz->name, RTE_RING_MZ_PREFIX,
sizeof(RTE_RING_MZ_PREFIX) - 1) != 0)
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v2 7/7] app/test: update unit test with rte_memzone_free
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 6/7] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-06 10:32 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (3 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-06 10:32 UTC (permalink / raw)
To: dev
Update memzone unit test for the new rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index c5e4872..7667d30 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -683,6 +683,51 @@ test_memzone_bounded(void)
return (0);
}
+static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[4];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
return 0;
}
@@ -795,6 +840,10 @@ test_memzone(void)
if (test_memzone_reserve_max_aligned() < 0)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
return 0;
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 0/9] Dynamic memzone
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 7/7] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (8 more replies)
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (2 subsequent siblings)
10 siblings, 9 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v3:
- Create dummy librte_malloc
- Add deprecation notice
- Rework some of the code
- Doc update
- checkpatch
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
Sergio Gonzalez Monroy (9):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove free_memseg and references to it
eal: new rte_memzone_free
app/test: update unit test with rte_memzone_free
doc: announce ABI change of librte_malloc
doc: update malloc documentation
MAINTAINERS | 9 +-
app/test/test_malloc.c | 86 -----
app/test/test_memzone.c | 441 +++-------------------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------
doc/guides/prog_guide/overview.rst | 11 +-
doc/guides/rel_notes/abi.rst | 1 +
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 329 ++++++----------
lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
lib/librte_eal/common/include/rte_malloc.h | 342 +++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 +++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 ++++++++++
lib/librte_eal/common/malloc_heap.c | 206 ++++++++++
lib/librte_eal/common/malloc_heap.h | 70 ++++
lib/librte_eal/common/rte_malloc.c | 259 +++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------
lib/librte_malloc/malloc_elem.h | 190 ----------
lib/librte_malloc/malloc_heap.c | 209 ----------
lib/librte_malloc/malloc_heap.h | 70 ----
lib/librte_malloc/rte_malloc.c | 228 +----------
lib/librte_malloc/rte_malloc.h | 342 -----------------
lib/librte_malloc/rte_malloc_version.map | 16 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
56 files changed, 1897 insertions(+), 2363 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 1/9] eal: move librte_malloc to eal/common
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (7 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
Move malloc inside eal.
Create a dummy malloc library to avoid breaking applications that have
librte_malloc in their DT_NEEDED entries.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing to free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
MAINTAINERS | 9 +-
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 209 +++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 209 ---------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 228 +---------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 16 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
43 files changed, 1446 insertions(+), 1424 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 9362c19..f108333 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -73,6 +73,7 @@ F: lib/librte_eal/common/*
F: lib/librte_eal/common/include/*
F: lib/librte_eal/common/include/generic/
F: doc/guides/prog_guide/env_abstraction_layer.rst
+F: doc/guides/prog_guide/malloc_lib.rst
F: app/test/test_alarm.c
F: app/test/test_atomic.c
F: app/test/test_byteorder.c
@@ -97,6 +98,8 @@ F: app/test/test_spinlock.c
F: app/test/test_string_fns.c
F: app/test/test_tailq.c
F: app/test/test_version.c
+F: app/test/test_malloc.c
+F: app/test/test_func_reentrancy.c
Secondary process
K: RTE_PROC_
@@ -155,12 +158,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
Core Libraries
--------------
-Dynamic memory
-F: lib/librte_malloc/
-F: doc/guides/prog_guide/malloc_lib.rst
-F: app/test/test_malloc.c
-F: app/test/test_func_reentrancy.c
-
Memory pool
M: Olivier Matz <olivier.matz@6wind.com>
F: lib/librte_mempool/
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 0b169c8..5d3cc39 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -97,6 +97,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -295,13 +297,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 5deb55a..810168f 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -100,6 +100,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -302,13 +304,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 97b364a..d45aa9d 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..2055539 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
+DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index 3d1d9eb..1a87286 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -78,6 +77,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 67b6a6c..0401be2 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -47,6 +49,7 @@ DPDK_2.0 {
rte_eal_tailq_register;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -62,6 +65,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -75,6 +85,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -88,6 +99,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 3ea3bbf..ffe94e4 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..defb903
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,209 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
+
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 077ea99..9ce50a2 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -90,6 +89,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 3696cb1..57454e6 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -52,6 +52,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_jhash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
index 947e41c..32d86b9 100644
--- a/lib/librte_malloc/Makefile
+++ b/lib/librte_malloc/Makefile
@@ -28,7 +28,6 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
include $(RTE_SDK)/mk/rte.vars.mk
# library name
@@ -41,10 +40,7 @@ CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
EXPORT_MAP := rte_malloc_version.map
# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
+SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c
# this lib needs eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index defb903..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
-
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
index c313a57..4b9dc7f 100644
--- a/lib/librte_malloc/rte_malloc.c
+++ b/lib/librte_malloc/rte_malloc.c
@@ -31,230 +31,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
+/* Empty file to be able to create a dummy library for deprecation policy */
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
index af6ae9b..63cb5fc 100644
--- a/lib/librte_malloc/rte_malloc_version.map
+++ b/lib/librte_malloc/rte_malloc_version.map
@@ -1,19 +1,3 @@
DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
local: *;
};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 2/9] eal: memzone allocated by malloc
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (6 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically
contiguous hugepages, memzones are slices of memsegs and malloc further
slices memzones into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 274 ++++++----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/malloc_elem.c | 68 ++++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 139 ++++++-----
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
8 files changed, 196 insertions(+), 317 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 888f9e5..943012b 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,15 +50,15 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
const struct rte_mem_config *mcfg;
+ const struct rte_memzone *mz;
unsigned i = 0;
/* get pointer to global configuration */
@@ -68,8 +68,9 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ mz = &mcfg->memzone[i];
+ if (mz->addr != NULL && !strncmp(name, mz->name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
@@ -88,39 +89,45 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
len, socket_id, flags, RTE_CACHE_LINE_SIZE);
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* Find the heap with the greatest free block size */
+static void
+find_heap_max_free_elem(int *s, size_t *len, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > *len) {
+ *len = stats.greatest_free_size;
+ *s = i;
+ }
+ }
+ *len -= (MALLOC_ELEM_OVERHEAD + align);
+}
- while (addr_offset + len < ms->len) {
+/* Find a heap that can allocate the requested size */
+static void
+find_heap_suitable(int *s, size_t len, unsigned align)
+{
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size >= len + MALLOC_ELEM_OVERHEAD + align) {
+ *s = i;
+ break;
+ }
}
-
- return (addr_offset);
}
static const struct rte_memzone *
@@ -128,13 +135,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -166,7 +167,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -180,129 +180,50 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = 0;
+ }
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- /* check flags for hugepage sizes */
- if ((flags & RTE_MEMZONE_2MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_1G)
- continue;
- if ((flags & RTE_MEMZONE_1GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_2M)
- continue;
- if ((flags & RTE_MEMZONE_16MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16G)
- continue;
- if ((flags & RTE_MEMZONE_16GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16M)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY) {
+ if (requested_len == 0)
+ find_heap_max_free_elem(&socket_id, &requested_len, align);
+ else
+ find_heap_suitable(&socket_id, requested_len, align);
+
+ if (socket_id == SOCKET_ID_ANY) {
+ rte_errno = ENOMEM;
+ return NULL;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
- /*
- * If RTE_MEMZONE_SIZE_HINT_ONLY flag is specified,
- * try allocating again without the size parameter otherwise -fail.
- */
- if ((flags & RTE_MEMZONE_SIZE_HINT_ONLY) &&
- ((flags & RTE_MEMZONE_1GB) || (flags & RTE_MEMZONE_2MB)
- || (flags & RTE_MEMZONE_16MB) || (flags & RTE_MEMZONE_16GB)))
- return memzone_reserve_aligned_thread_unsafe(name,
- len, socket_id, 0, align, bound);
-
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0 ? elem->size : requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -419,45 +340,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -465,14 +347,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -485,33 +363,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index defb903..f5fff96 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,104 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, size_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned ret = 1;
+
+ if ((flags & RTE_MEMZONE_2MB) && hugepage_sz == RTE_PGSIZE_1G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_1GB) && hugepage_sz == RTE_PGSIZE_2M)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16MB) && hugepage_sz == RTE_PGSIZE_16G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16GB) && hugepage_sz == RTE_PGSIZE_16M)
+ ret = 0;
+
+ return ret;
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -207,3 +187,20 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
return 0;
}
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 3/9] app/test: update malloc/memzone unit tests
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (5 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 440 ++++--------------------------------------------
2 files changed, 35 insertions(+), 491 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..6934eee 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
-
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
+ maxlen = find_max_block_free_size(align);
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -762,282 +680,6 @@ test_memzone_bounded(void)
if ((rc = check_memzone_bounded("bounded_1K_MAX", 0, 64, 1024)) != 0)
return (rc);
- return (0);
-}
-
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
-
return 0;
}
@@ -1125,14 +767,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +783,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
` (4 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
During initializaio malloc sets all available memory as part of the heaps.
CONFIG_RTE_MALLOC_MEMZONE_SIZE was used to specify the default memory
block size to expand the heap. The option is not used/relevant anymore,
so we remove it.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 5d3cc39..cf495ad 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -98,7 +98,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 810168f..5e81614 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -101,7 +101,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 5/9] eal: remove free_memseg and references to it
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (3 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
Remove free_memseg field from internal mem config structure as it is
not used anymore.
Also remove code in ivshmem that was setting up free_memseg on init.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/include/rte_eal_memconfig.h | 3 ---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
2 files changed, 12 deletions(-)
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..7de906b 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,9 +73,6 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors - NOT USED */
- struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
-
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
/* Heaps of Malloc per socket */
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 6/9] eal: new rte_memzone_free
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
` (2 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 +++
lib/librte_eal/common/eal_common_memzone.c | 55 +++++++++++++++++++++--
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 8 ++--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 +++
6 files changed, 80 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 0401be2..7110816 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -105,3 +105,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 943012b..dbb3844 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -78,6 +78,27 @@ memzone_lookup_thread_unsafe(const char *name)
}
/*
+ * This function is called only if the number of memzones is smaller
+ * than RTE_MAX_MEMZONE, so it is expected to always succeed.
+ */
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr == NULL)
+ break;
+ }
+
+ return &mcfg->memzone[i];
+}
+
+/*
* Return a pointer to a correctly filled memzone descriptor. If the
* allocation cannot be done, return NULL.
*/
@@ -141,7 +162,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -215,7 +236,9 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -291,6 +314,32 @@ rte_memzone_reserve_bounded(const char *name, size_t len,
return mz;
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_read_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ mcfg->memzone[idx].addr = NULL;
+ rte_free(addr);
+
+ rte_rwlock_read_unlock(&mcfg->mlock);
+
+ return ret;
+}
/*
* Lookup for the memzone identified by the given name
@@ -364,7 +413,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index 81b6ad4..227306b 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -240,6 +240,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = mcfg->memzone_cnt;
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < mcfg->memzone_cnt; i++) {
mz = &mcfg->memzone[i];
/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 7/9] app/test: update unit test with rte_memzone_free
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
Update memzone unit test for the new rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 6934eee..501ad12 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -684,6 +684,55 @@ test_memzone_bounded(void)
}
static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[4];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
test_memzone(void)
{
const struct rte_memzone *memzone1;
@@ -791,6 +840,10 @@ test_memzone(void)
if (test_memzone_reserve_max_aligned() < 0)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
return 0;
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 8/9] doc: announce ABI change of librte_malloc
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
Announce the creation of dummy malloc library for 2.1 and removal of
such library, now integrated in librte_eal, for 2.2 release.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/rel_notes/abi.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/guides/rel_notes/abi.rst b/doc/guides/rel_notes/abi.rst
index f00a6ee..2aaf900 100644
--- a/doc/guides/rel_notes/abi.rst
+++ b/doc/guides/rel_notes/abi.rst
@@ -38,3 +38,4 @@ Examples of Deprecation Notices
Deprecation Notices
-------------------
+* librte_malloc library has been integrated into librte_eal. The 2.1 release creates a dummy/empty malloc library to fulfill binaries with dynamic linking dependencies on librte_malloc.so. Such dummy library will not be created from release 2.2 so binaries will need to be rebuilt.
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v3 9/9] doc: update malloc documentation
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
@ 2015-06-19 17:21 ` Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-19 17:21 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/prog_guide/env_abstraction_layer.rst | 220 +++++++++++++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------------------
doc/guides/prog_guide/overview.rst | 11 +-
5 files changed, 221 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..f3506081c91feb7b80b0389c80e06addafcfb907 100644
GIT binary patch
literal 80952
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
literal 81329
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%0AJ1PuixJX??D}PX8AKL
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
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 3295661..9fa4031 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 0/9] Dynamic memzone
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (8 more replies)
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
10 siblings, 9 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v4:
- rebase and fix couple of merge issues
v3:
- Create dummy librte_malloc
- Add deprecation notice
- Rework some of the code
- Doc update
- checkpatch
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
Sergio Gonzalez Monroy (9):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove free_memseg and references to it
eal: new rte_memzone_free
app/test: update unit test with rte_memzone_free
doc: announce ABI change of librte_malloc
doc: update malloc documentation
MAINTAINERS | 9 +-
app/test/test_malloc.c | 86 -----
app/test/test_memzone.c | 441 +++-------------------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------
doc/guides/prog_guide/overview.rst | 11 +-
doc/guides/rel_notes/abi.rst | 1 +
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 329 ++++++----------
lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
lib/librte_eal/common/include/rte_malloc.h | 342 +++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 +++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 ++++++++++
lib/librte_eal/common/malloc_heap.c | 206 ++++++++++
lib/librte_eal/common/malloc_heap.h | 70 ++++
lib/librte_eal/common/rte_malloc.c | 259 +++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------
lib/librte_malloc/malloc_elem.h | 190 ----------
lib/librte_malloc/malloc_heap.c | 208 ----------
lib/librte_malloc/malloc_heap.h | 70 ----
lib/librte_malloc/rte_malloc.c | 228 +----------
lib/librte_malloc/rte_malloc.h | 342 -----------------
lib/librte_malloc/rte_malloc_version.map | 16 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
56 files changed, 1897 insertions(+), 2362 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 1/9] eal: move librte_malloc to eal/common
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (7 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
Move malloc inside eal.
Create a dummy malloc library to avoid breaking applications that have
librte_malloc in their DT_NEEDED entries.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing to free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
MAINTAINERS | 9 +-
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 208 ++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 208 --------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 228 +---------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 16 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
43 files changed, 1445 insertions(+), 1423 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 54f0973..bb08e0a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -73,6 +73,7 @@ F: lib/librte_eal/common/*
F: lib/librte_eal/common/include/*
F: lib/librte_eal/common/include/generic/
F: doc/guides/prog_guide/env_abstraction_layer.rst
+F: doc/guides/prog_guide/malloc_lib.rst
F: app/test/test_alarm.c
F: app/test/test_atomic.c
F: app/test/test_byteorder.c
@@ -97,6 +98,8 @@ F: app/test/test_spinlock.c
F: app/test/test_string_fns.c
F: app/test/test_tailq.c
F: app/test/test_version.c
+F: app/test/test_malloc.c
+F: app/test/test_func_reentrancy.c
Secondary process
K: RTE_PROC_
@@ -155,12 +158,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
Core Libraries
--------------
-Dynamic memory
-F: lib/librte_malloc/
-F: doc/guides/prog_guide/malloc_lib.rst
-F: app/test/test_malloc.c
-F: app/test/test_func_reentrancy.c
-
Memory pool
M: Olivier Matz <olivier.matz@6wind.com>
F: lib/librte_mempool/
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 464250b..2b0c877 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -102,6 +102,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -300,13 +302,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index aae22f4..fc6dc2e 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -105,6 +105,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -307,13 +309,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 97b364a..d45aa9d 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..2055539 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
+DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index c73ffb6..0cc9f8a 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -78,6 +77,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 67b6a6c..0401be2 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -47,6 +49,7 @@ DPDK_2.0 {
rte_eal_tailq_register;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -62,6 +65,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -75,6 +85,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -88,6 +99,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 38772d4..0c43d6a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..8861d27
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,208 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index e99d7a3..9e66442 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -90,6 +89,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 3696cb1..57454e6 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -52,6 +52,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_jhash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
index 947e41c..32d86b9 100644
--- a/lib/librte_malloc/Makefile
+++ b/lib/librte_malloc/Makefile
@@ -28,7 +28,6 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
include $(RTE_SDK)/mk/rte.vars.mk
# library name
@@ -41,10 +40,7 @@ CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
EXPORT_MAP := rte_malloc_version.map
# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
+SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c
# this lib needs eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index 8861d27..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
index c313a57..4b9dc7f 100644
--- a/lib/librte_malloc/rte_malloc.c
+++ b/lib/librte_malloc/rte_malloc.c
@@ -31,230 +31,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
+/* Empty file to be able to create a dummy library for deprecation policy */
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
index af6ae9b..63cb5fc 100644
--- a/lib/librte_malloc/rte_malloc_version.map
+++ b/lib/librte_malloc/rte_malloc_version.map
@@ -1,19 +1,3 @@
DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
local: *;
};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 2/9] eal: memzone allocated by malloc
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (6 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically
contiguous hugepages, memzones are slices of memsegs and malloc further
slices memzones into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 274 ++++++----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/malloc_elem.c | 68 ++++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 140 ++++++-----
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
8 files changed, 197 insertions(+), 317 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index aee184a..943012b 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,15 +50,15 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
const struct rte_mem_config *mcfg;
+ const struct rte_memzone *mz;
unsigned i = 0;
/* get pointer to global configuration */
@@ -68,8 +68,9 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ mz = &mcfg->memzone[i];
+ if (mz->addr != NULL && !strncmp(name, mz->name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
@@ -88,39 +89,45 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
len, socket_id, flags, RTE_CACHE_LINE_SIZE);
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* Find the heap with the greatest free block size */
+static void
+find_heap_max_free_elem(int *s, size_t *len, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > *len) {
+ *len = stats.greatest_free_size;
+ *s = i;
+ }
+ }
+ *len -= (MALLOC_ELEM_OVERHEAD + align);
+}
- while (addr_offset + len < ms->len) {
+/* Find a heap that can allocate the requested size */
+static void
+find_heap_suitable(int *s, size_t len, unsigned align)
+{
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size >= len + MALLOC_ELEM_OVERHEAD + align) {
+ *s = i;
+ break;
+ }
}
-
- return addr_offset;
}
static const struct rte_memzone *
@@ -128,13 +135,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -166,7 +167,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -180,129 +180,50 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = 0;
+ }
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- /* check flags for hugepage sizes */
- if ((flags & RTE_MEMZONE_2MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_1G)
- continue;
- if ((flags & RTE_MEMZONE_1GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_2M)
- continue;
- if ((flags & RTE_MEMZONE_16MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16G)
- continue;
- if ((flags & RTE_MEMZONE_16GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16M)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY) {
+ if (requested_len == 0)
+ find_heap_max_free_elem(&socket_id, &requested_len, align);
+ else
+ find_heap_suitable(&socket_id, requested_len, align);
+
+ if (socket_id == SOCKET_ID_ANY) {
+ rte_errno = ENOMEM;
+ return NULL;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
- /*
- * If RTE_MEMZONE_SIZE_HINT_ONLY flag is specified,
- * try allocating again without the size parameter otherwise -fail.
- */
- if ((flags & RTE_MEMZONE_SIZE_HINT_ONLY) &&
- ((flags & RTE_MEMZONE_1GB) || (flags & RTE_MEMZONE_2MB)
- || (flags & RTE_MEMZONE_16MB) || (flags & RTE_MEMZONE_16GB)))
- return memzone_reserve_aligned_thread_unsafe(name,
- len, socket_id, 0, align, bound);
-
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0 ? elem->size : requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -419,45 +340,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -465,14 +347,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -485,33 +363,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index 8861d27..f5fff96 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,104 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, size_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned ret = 1;
+
+ if ((flags & RTE_MEMZONE_2MB) && hugepage_sz == RTE_PGSIZE_1G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_1GB) && hugepage_sz == RTE_PGSIZE_2M)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16MB) && hugepage_sz == RTE_PGSIZE_16G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16GB) && hugepage_sz == RTE_PGSIZE_16M)
+ ret = 0;
+
+ return ret;
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -206,3 +186,21 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
socket_stats->alloc_count = heap->alloc_count;
return 0;
}
+
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 3/9] app/test: update malloc/memzone unit tests
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (5 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 440 ++++--------------------------------------------
2 files changed, 35 insertions(+), 491 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..6934eee 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
-
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
+ maxlen = find_max_block_free_size(align);
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -762,282 +680,6 @@ test_memzone_bounded(void)
if ((rc = check_memzone_bounded("bounded_1K_MAX", 0, 64, 1024)) != 0)
return (rc);
- return (0);
-}
-
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
-
return 0;
}
@@ -1125,14 +767,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +783,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
` (4 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
During initializaio malloc sets all available memory as part of the heaps.
CONFIG_RTE_MALLOC_MEMZONE_SIZE was used to specify the default memory
block size to expand the heap. The option is not used/relevant anymore,
so we remove it.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 2b0c877..a54957d 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -103,7 +103,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index fc6dc2e..72611c9 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -106,7 +106,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 5/9] eal: remove free_memseg and references to it
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (3 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
Remove free_memseg field from internal mem config structure as it is
not used anymore.
Also remove code in ivshmem that was setting up free_memseg on init.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/include/rte_eal_memconfig.h | 3 ---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
2 files changed, 12 deletions(-)
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..7de906b 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,9 +73,6 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors - NOT USED */
- struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
-
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
/* Heaps of Malloc per socket */
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 6/9] eal: new rte_memzone_free
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
` (2 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 +++
lib/librte_eal/common/eal_common_memzone.c | 55 +++++++++++++++++++++--
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 8 ++--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 +++
6 files changed, 80 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 0401be2..7110816 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -105,3 +105,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 943012b..dbb3844 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -78,6 +78,27 @@ memzone_lookup_thread_unsafe(const char *name)
}
/*
+ * This function is called only if the number of memzones is smaller
+ * than RTE_MAX_MEMZONE, so it is expected to always succeed.
+ */
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr == NULL)
+ break;
+ }
+
+ return &mcfg->memzone[i];
+}
+
+/*
* Return a pointer to a correctly filled memzone descriptor. If the
* allocation cannot be done, return NULL.
*/
@@ -141,7 +162,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -215,7 +236,9 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -291,6 +314,32 @@ rte_memzone_reserve_bounded(const char *name, size_t len,
return mz;
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_read_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ mcfg->memzone[idx].addr = NULL;
+ rte_free(addr);
+
+ rte_rwlock_read_unlock(&mcfg->mlock);
+
+ return ret;
+}
/*
* Lookup for the memzone identified by the given name
@@ -364,7 +413,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index ee62680..5223b1e 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -240,6 +240,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = mcfg->memzone_cnt;
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < mcfg->memzone_cnt; i++) {
mz = &mcfg->memzone[i];
/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 7/9] app/test: update unit test with rte_memzone_free
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
Update memzone unit test for the new rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 6934eee..501ad12 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -684,6 +684,55 @@ test_memzone_bounded(void)
}
static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[4];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
test_memzone(void)
{
const struct rte_memzone *memzone1;
@@ -791,6 +840,10 @@ test_memzone(void)
if (test_memzone_reserve_max_aligned() < 0)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
return 0;
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 8/9] doc: announce ABI change of librte_malloc
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
Announce the creation of dummy malloc library for 2.1 and removal of
such library, now integrated in librte_eal, for 2.2 release.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/rel_notes/abi.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/guides/rel_notes/abi.rst b/doc/guides/rel_notes/abi.rst
index f00a6ee..2aaf900 100644
--- a/doc/guides/rel_notes/abi.rst
+++ b/doc/guides/rel_notes/abi.rst
@@ -38,3 +38,4 @@ Examples of Deprecation Notices
Deprecation Notices
-------------------
+* librte_malloc library has been integrated into librte_eal. The 2.1 release creates a dummy/empty malloc library to fulfill binaries with dynamic linking dependencies on librte_malloc.so. Such dummy library will not be created from release 2.2 so binaries will need to be rebuilt.
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v4 9/9] doc: update malloc documentation
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
@ 2015-06-25 14:05 ` Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-25 14:05 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/prog_guide/env_abstraction_layer.rst | 220 +++++++++++++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------------------
doc/guides/prog_guide/overview.rst | 11 +-
5 files changed, 221 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..f3506081c91feb7b80b0389c80e06addafcfb907 100644
GIT binary patch
literal 80952
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
literal 81329
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%0AJ1PuixJX??D}PX8AKL
zjh5($@)s^R1*ogu;au*l%mcB_kBw(2nYVSn+OUAuU?M`LV<V?<RiyqKO2Uy=9oe_A
z@P(;r$fuW5nPsBV@|jy#faYWKH>D}wUr|mw8?Vd<*UXd2b?U}qHiCo#DzrW)g#24x
zYI+1yo4Nj%I(oQOWB)4z#gpCaZ8$#hhB@7rk5Ar620lcTl#ra81$t$yG?mK4Nkdq@
zWu4ra$%wSY@Yk}D9hp8ktyqSJi4tQ!c7tgz4LjG3o7Mdu?P&k8n3(hCN@3hAVPyR_
zV97;G(_L#;L+hWPX2;EsL$O+Cyh^3kSu<t`T1v89O}S|lrVSO`##0MZuJgOu0Ah}3
zi;(1vWzA{`1J%T>l&tu>bb3iT7PUZ3iM=MYBq?U1v{VztTBZ=BrQhbmxD&+;XWOK9
z($k)?GLuLdlMg6GJ0q-9hTpkl*ocZqd~47sJw8(3f1F<d>=`nA`sx6t*6Te1BVPBf
zNh47GZ7-Ce0q1$Q9o5BbIaL;Vu2UpW_hJ@i6eKZ`B8KBldyRG%3gyea1Ro3Hhs+)k
zDfD!qJpE5-BuCt`o0%b3)Sm{ARklXu>Q5^Ov6wN;&5Lr9;Rf9WPD6u$qSbz3dwmk?
zU#aEGc#Wr!`Q;DUqIKGJ(Xp7+y;04F_N4KUrfS+NB1}LS!8ua`Pe9W2@>0a!x|+P@
z>aghGRXEmdANCRVt`tL7T)Jr$LHWcymb0<x1=f)yS2vk_%8NdSaQrlk_oE5G0W4=}
znLY!!#LW+Mrb#_WMb1&1C;ilkdUg*r`XDvI3;#{RKjV7qsyVk(FKqkVG9S&Al7k=F
z($GreKT?@N0iNz<is8BBm}9T@z!XJt)80|5P@SuE1}<TaY&+6fDIIoA@!v?}UgM7u
zp22auXptxfV<*XT)%CCL6&GJ&8eq-4AN2H(p*tYYDwwDb^?VA!GW+l$>~1qZENQ}m
zgV9ocvoA~M_30@a=b#Z_oIHR8606rC8xlJqVYh3WXTycq(9CP!lP78%8r#PB{Zh{A
zSX10lBfKa@#*$IK2a_@vb0m79+=C)*{mQ*=Npbqfe)}>qAZI#GAL;1e>DE$d$8K|3
zNW{#YnVr?i{R=ggHoM+Ba}8f%63!)M)MZZ@HVo&;s2}dy7<5}H%$aTh5z8xGk2-`D
zE)5s?UWiPA2*~6%M_9h+<S_BKR`9W;c2?smy6g)xDNe{iPthjArxn?0n=qDitO?Fn
z&$f1tG+FJrr%cPl-31!<tW-$lh~-qT3C1(w$pbY^Z-*5H7U_-tc5R#OCZIswmEuR(
zvY#!=xyIIcQcytTc!X|t<jN%eJ!xvJQALcx^Mr2jXeawAy@{Yq3eD8jD72Mh=@k0e
z%oP&ow_Ud~Z=iySDq0Q(G9a~yc2*dUJ$tl)nR8+Qye>Ny;C>1EG80ewtmVFREo&^j
zE0ked0g?SL_o!bx4;Su)eDpE4YZ{517e5ZtD*O-=`OS8Z2IL64?<Q|4i(L@1aIz);
zu8TAqJ9y{pZRqE*McH(B^^EA#Vu9)DVL@yP&zPvX_=CGY69QgACVfgK$o3JfR3Q1&
z7szXYhSGiqbu6s`EUb}z?XU1-qNKn8AyVyKH~z&Lf;Dt<#znz=sxV)dj3abfv-jUc
zC;(`)iByhRep~G4dxnUOrX9X}0*%|ou$qvfnt7(tw)ZZWjhnTth1%5U_c~R#V-2Uh
zoPMgnIe>w9Msw^Zch|Ka#%#pg`IEi$EBC|}W)4F$=cq(RCb}LuE2-(2zAtA6uW(!4
zF&;duujgA*vnx8w^JBjW#LtID7zqSvOOp}ucm;F%DJON2q_5lAm}T~rDG$+GR`c;n
zWva8UsK4~GQnyXn^!}(M5?EI0WxcHi4fa+rQU-cp)dQf9KSG)?ZoG7GxVl&?!_rdb
z7kOEJV^nHq<<1;}x$pnqvdxp(0%;%dlfI_V1XxIeY*7s(JxPEAG~MKFJ0y67(%}eW
z-nr5*Yws7zShrA=$=gqt>Lpq~zlIiCnTO29%37#bXrM$-MmxwfAkT?_=@dsk*QsUv
zpN^)sbP|4&GJ;QLGauQ?^S_{j6;uBye#?T+6+2hwo_q6W6dmuo;vZ{~Fte+j($(`k
z$?wSaBb`I2<2qnj=hafX{apo5tO=~0a0;;Of4He9D^=3kG?y`$ZGC(cQ$6!Ar}*cu
z_b!nO`@yet5aa$@a!*583^QLq+H$A-wSGwbd35^iUTM2(xik_%-%GPLm$193pHE)D
zQf+}%`FGS%j8EindoJH9C4@$a(;*sWBy(&}pTFqtu@oYHpB40Dxoy_8Fj$)Af;Q2~
z)i3{x=h^$k^caj&9>td|q}q$elQPu#nYvO5abGiadBng%f;xZCkMJVf+{{jNRIdnz
zDxd1#QNp=*>yl6tm)@&iA2-MiU{7mkMUF2hA|n@vV3Eivj#?t%a~2`TuZpQ>$VM2v
zTxa8KXvm^7?mSHuvi0ceG^j3{^GvIPu8^HMYd7x^tH#bsS>%rkcoOuF5wF%^pu5}2
zTCbPC6$rr&<nI5}HpLr+=`PzqR!{@uN#N>m`FaPKiDsi(9I48Fr-`S)#hJ-XpWB)B
zTgZ9ZSv|_jrm<|1sRgS?k^N^%r?Z}2xkW}GMdz<P@?rzh&OGopGD-?G1aI(rSg;E;
zWHPN5yLB*d07firt?G^W0(QCkXItcgPln_bNK;FeN~lK$Z2z*1ar<&qOU3%m;=3eU
z$`)%3mNZUv0f;&uPN{9k+FKW8o?qs$&676M=6ictsoT(+M~IECET78$qy3vaV#~zn
zX)I&^1U}per?oC6bIyrD|K)S#ZLzNk0gmGB*_IfWMC?;rG`R(n(@!<_LQ%FXkc-NH
zl&nY2y%yi+LNopO!c<CNOXZ0u)_hUdqxX&unphQy^$Rn0i?l7tP6<bLR@_=B*_fnF
zlIiAHC?&?<pM_Wr%>-5SB~Z}@3w~P0M<?`=K_6s!UU~GZ-2lr}1)3zAli>8RUR9vs
z#wR}2&{Nax;8$6;L{A=JczX>8_qKzG+B~1QKtaXVx6SXS)_=i|_7i9xEUR6zOnaWw
z>?AMN?+X2H^41u8SP9$cqj4DoXdFhw7+!q%2HzK9GU}!5pOlz?irrRySUA(!oaN<4
zChm}qaECBzKbw=MvlD_{3xi8<={H@$?d9NrI9LHaIJ~-F3=}C4FDq%?HmM<j`ya9p
zFOyI&X{_75qX+qv0ddzunp^6)R6{?_p`qcYmh>5n$CSsB&i{O`hV|^*SvuNEv=I3p
zrUW8`-p_d{?5t#_0XWEWHVzIy9NiZpRpIy|J_6Vdqj3lM;77u)q4xS~qV@!!Y}GTl
z%)&5H2Q5@Ord`#oF`hfu8WX8XVG^~r{FVEo6^ZXHZFvkAHp#U@`f9t(K_P{=7c!<K
zw)raE;4SOxy|$d9_JUuG@b_kqw8>UPY;Dbp%$=s*FbxBle`AXVZR4=TYlEcYZ^$dU
ztyY|bS$Ins`|Orq*?^5~(<AGWQen-E*JlJ!48lJG$!op<AhqkxG>>>eB>EtD5-&u~
z>5mo>`v;QO`8#_rJFCwe#bVxYjC4>N$Z)<WUg}MC&q!=l_l!+8ds|GHG|S(?u{y9^
zxHdp^I%l1^c^&PaNW$OtJTqsRp0oY<)4^Pm{AH>AZjM+1G%#dhV%TOr_FojgiHpm^
z3`!S7A@8YOPxMd>)jWtxHd49rG-Say@dSgY8yBOQ<|k_m&~Ii77mQO0cWAC8#m&{Y
zID>AQVQPuL`_3_s1tE)L8#bsA5-<%~OWic^_S-1f290Ycb}cGXP@LZ*j&+VarozTF
zaIOx%J|5#RtbG-$PbuD^?_aIv85Qq`-qo(Aw#AvogkPj0dOrfY-v2&*TB!!j@#C+2
zuuHPF@8FOQvLBrIDPTUEB5+}J>vy-j*z$GAnIbpA|MpwMY1I5t+^Bsk!@S!;!smIn
zp_Q*xwYIlPp6j2#F%aO&Ya95ll=)p;>3CY@8PWOAGt1BLm}mes=W<wB&l$=m>5!xT
z4#a)O6=&)SZjz1hBnmecgy_rw^ApEetp|Q*pc+?w<{S<{3~8bMR}*=>u;;$LBm;jC
zZ4=7UvVfsKc*`=~aY`26d^~s76#3#YDJ5jQt9HGzH@z9O?JFzp^YSworq)r@^N#V^
zspQ%C{<;X-;ql70pw?lm^g+b`Ym@zbLqB^Ogi~~$)B2}A?Y`d)PdZ<VhTIVGKxI>r
zn&>x$8Hrk#-!GkR_UbmIx3kztd>UW=6Z?0EwE=97Nr_70uQ}}QM6NHtRd3QCJ$VUs
zE<3O}mdS9n4jZOzw6#_${HVKgRJZnwBB!QK0nsx?)z1<}!JGMb6{Gu&vj)R!qzt>G
z7mG6qJ}6uM4xxE164Si2^x!D{TV*mPHKWLGxJB6E$|b2=`yrnA=?%4qY-mzm2`};Q
zZx*pNEH7n@@NJQU#rHW6i{^3kw0V_$gQB8}YHQWRFWqTUf%ms^cAk5DDiZ$`mv#u6
zDdk=7+ppyLe1<7j^z;ib@4UaB&V8T<zJ{q;$Td!-($;9MJ&4y<d>@oK*I_%)0fBg*
zb}6zip{2bds&L-{YHi%<sS^*Q_e*&dF3HXb^4xL{m(DlgBA~u&lSUt;eO_wX$&PO7
zwPvBZXJ-|aWGrDZW+RketUF$ZE_VFo>!Y-To<q7HrP#Q*Lu_&?U3XEhuBDb7$HS=6
z2Z3e5yq$pe=)U_1WXkz*aJC)?rblk@w74Io#4kc>nQ#OeNIStdt8J1K8gvwBNRxnH
z%W^&I8F<Zlbf%!Df*HD5>~_@7O%^cB!y~HcwAs%<Z`%jYQIV283~0i9`X%(T@Ti!W
z?Fk9dc*0fgow(4}qWH9^Ie~4U8tIqfPM~P9;z^w6squatIzV39dV#WAikX}BWv>n7
z{{MaQZl{;wIu?9HiQ50;>n)?Q+@h{w=`LyM?pC@*y1PS4q(r(~knWI{ZfT^uq$Q*q
zq`T|gx954@G2Zcw@%=mpxw-Cr?Y;I|bImp9=c*P_#_w9fjm5#kVb|uMhksV#+A7DE
zmn;VfyOP)A-RRS$Zewa_M0D=hoLC0!mbeUS;@%Hx-tUH8Kh}H9B2sX@1)}jXU!Sat
zP)SwjvfJHuMh#*<<(0MQ1GIqr@sW6e2MNyfSXhVMw=IF*9}A_B<{W5F!5|~TGDUb1
zH$ITNUr+Q8s7R6Y0z(n#;egeGD{>8lwK-<oR5S+sRe`(&^{F*|=rA}*7UP$2r9`~k
z9f*b%Dj@6j`Oyw02xq<kFC9$F6O)w&S9M7#m%<A9v|Xv`5PwWgE{Ub7l6JSOOF`61
z`a#qUWRz{9Wh?d_OuJILNY$71$A5BI5Yctp^oNFIotNXFOsxCIN%>Cm;`8b*f1R}+
z8_+RnRlGKp5r)zE^+`=2JqdUxp^$z2xj93Si>s~wk|@DCMPh3&)Bxm0DeO-3A0g=p
z7P}lhYo;6|cOZAOG%1a&oH98dy0~UcY#`X35c+XD((s)Go=^-a+&n+uL*>V`rMGkv
ztKN6dqE_}wn<zm7<iXkYdda6XGGQ7ek;hs{TA#8z@-BlRBbUyjkg`Us?oZTRK9272
z)__a4boV7Qx8QZG6lv;s5Ia8PBB*H1^m-q7K)l4H=NerUqEvI57a(?qLywQi4oNW<
zVUOAH3Io}%`c9;WGGL{npH$sqRS_hOX+@jASA<L?K0VQy-Pco$3Dro$VW_5%qszg{
zzo)y!0O`E)0#WnzU${6m2et4f7i0I-3R9FCucI!&>n`V^U({ZJrd7EW1*`GF?X-J@
z;hTA*pj@$0za%Y@fp~SOHcy>;9@5~OY;tCS)fY=T$`w2Bi>`Tl?UQ2p&sX?T8d1^h
zVu65bkS5;bo5yimoIy|a;};9!ZUM5>3WsHs?mqi2bLBx-HlR>>!(!;Zh5NV^yr2_t
zw1o{xpF3Gn^}cV>(i82X8|TeaNk~^f<(MUX%>L~y1-2<nn{CSei~O!n{h3W^x}lHV
z-BhW*&(oJDx6G`W8LF8s(NQuQ)1Ky+dt6&!vI8$`%(73O15$O|N*G3#>v)XNQ;oXF
zbE3<qxg3*DC_<^0hiQKp`>d8`i!v$p^J!t_slZ&dtS8dh`m@7JS55#M_$7ssxu|Qj
zVAUxhI!G~1PURyF@@=OWzFZrp)?k4;7BaktL)kXJderK|8BR+`U=1PhX}p5na;APC
z4V38lHkJ9Fg02VWEHhYsCA0CE7Q`7^T{+8{Y9}HL!RjgJRTQXEhE{xt1$kKUwAG;c
zi{2M@9~+`0KBlt3WvHihn9_JQ7WaBEa-nT1rFaE*n;0DDs2i{CcwL2Rg5yIwoNdCS
zg0|#Umvtl!TdOF3hHKD1p~u4%iDa=Yppm{2{~K}8ocVX|qg-U>Lc*~BbTdYkZ~th9
zhlnYCPDx9PyrR&h`t2&;j<l6ku_at?i&;{TxcBFCB_C?7dIJV9p^-w!6)eFm>+765
zrIpg3T`$*nrz?RlC&>=Xj7o|Qs7T(a{w|)z3|3YDzD9g14aeM9v@|u!B=<|<u;u5T
zKwg_ASKZ#51mxjIZ$Ov2*gNMH21avr#ZO(q8~aV0OmH*|8*nQpHya*t*GX%!I0l(u
z97|lfE#OW_ho{cdae#%K;uc;K`Q)grn#-A|?votKr5S}hcOU0Xxmv*T__&=rAa8Af
zl*{b?$~LogvqRT$e@3j%b~@gqFkh<aeZG3IZm*U)m5M=Ua9C2?sY+B;_RX}fEsOG7
z%1IqHx<?Wds!?HR@woR>2C=j1t#h%UetUn3o_!>LolZh8Dd2Jt0~lKa5LrusLw0wf
zTwLWcwWo@0`lxoiMYE+(-DsbIaMZ6#-$(18fU9o~1PjlA_Rlwl&)ALiObkqkaqE=j
zbKlPj-sY&ItEOagfZa+(Q$its4HEWY)PKcE$SB+FN~yXed)V}S^pXE;UD`bAl^A2M
zdD{>MKiRU^hC*wM5#!8@nyQn+!3(cI>wLLH^Kr5`&8#=jvGQ|%;uEZiCcm8+s|}mp
zCROJY*<$(@>CLEhpTI8GB^BAa@9MXHFl3}6P%deRK8W@9j8qBQeL<^CQ2ygd++TP*
z#kBT)Y^k-5JbZt{Jw&&a&G+`#Sz@KvJBVcU?Ma9TGuhpE$R@~QV%fabP|ob1uk6o&
z%<Q*fZ2z223oE;N!_h`0)e%PYwGoRVZ}G(9t?LrU@(&wZ3d?Dj5Q!8zk%u*m%bLYg
zqvw2l8+U60l*9Zv>3mO83LS9ExIF`bd?rsx$zgCsR`^|C1DT82u0Q*4n@1F(k5`!U
zLg;HjT18CYpDMa#?c`M1_5{=^orGJft<BUG%PW%De7BIneA38;!kOljiw+ewfq#+e
z(3j>^HTxPR`1!k@Njg+$)xyl_82`N~tW)GgtYdW$38gN)zE3SpB{BBay2*~j{s<*o
z=84e!`*5_#07N0<y3qyXpIOX^aU*W<pKG!n%e;2wj3(>jA#x8}bRxH3pZ3u%`QH$*
zSvKYx?e*2c;Z9e+_QsusGcaPnP}smO>55#M%zOQH9=;vcgBi}OwFsw*JC?O$cmpv1
zV(q;)i01cUai~yBGb<BNKXl2-$*N9%F_i}H&(*{&JRZD+3NM6184ziMVp0)SfI&mj
z?r6Z-dwFM#<4?$7QC_3wPPr5MJev5_tq)L{U+R!0(8%Gr8=4ThGqNxw-kxt;=hz-C
z{*>%-0*<QuCHLmJZ|Uf^O=`8iybI^zxS=T;5-G`x=w%}BL37g#NX~zE0-L|bH8jTb
zN$q~Io&@Al!*oOJ?6ci1$n<zK_Th;Q9R2U3;yAvB2biGImFw2YM)B{_f0dy07(fW?
zHE>;`=`>b;_3D+=x6PaUPOmja-T5A}zl5_-rnJhz=H#!1adxfIWImAFpfY^hk(hA}
ze?3ElU8e6xI0cxNEI>80!fQ`0E6y4)ATSgjXMG=&FD@=Z?YEjW7&T<-UgRsuq^dLW
zQTz<mWSR2h)$D0flxFq6*1UhzT46{L(6Rg2>T{&7>d!H0$6TAbHf!|N#KUL6NA4=V
zn)8@8h2822B$@OpE!#wsGc&X>FfbOwIFk!yLzF(X(4S=!%Odc_)pb`y(=K|g(Qz*)
zwMk-JejNl<*s4KS|2;Wc;%wr%{3-fL&C~45($x2(uGRy<B{icz+1dhG8T8>avk_kZ
zFKTQY)Rq%~{QYyH)uj*NJOzWazZ%*uS#Gy^+KaVb=>sC9>#XiHsQuWv6o1beK-EpI
zVn{35OQ1edUcpIXAkzN!I&#d42W)Ybz5V?yz$b45hsQStLc4yt=@<E!{}(&!CMex$
zQbtx~zJzJ{=rtoV1zeyH!nA<+V?a#0>8MS&q4#X2%975DlcjU$IEt-@Gj~bR0%d5z
znVCN=?lli<*X{8Q%dc&wS_9r-F&CIm+Sw+XI74kQH(g41!_L-X?b=|3!tUAZP8F2D
zVa&^Cqt_@Grjl=N&9`2V_>-te4^QK`GZYnzHCMw{12Bm(0>|(SQPSEP>^iZ*&i8Jg
z*ib+oW6sf!6xIucK%7lyi)~3uPoS;y2{dn;Rv8%?&45?R*^bDw&RBmV#EBUc3vG{w
z_SR`!_bA7*MH(~T_B`@x44|LjlaqhZCCdJ8D9d5L?)rSL*<A4UZXvDKed?!F#1Ifu
zD<)!)7w?b3IdcLY4KlvI?N=-h=ULDCW6h%43JMBN&JwR>Lr7gx5{4N;Q3^6BOgg7W
zO(I&4!iZbN5ylQA+aNQPciTK4t#rO#<zde?85sU)Q`>gCbt%43+bD3!V%BX`mX+Hc
zsy|$C8hagN$Cj<*FB&p(*26{8;}Ozjp@#l_-&pkPk3x=yqVfa|okhe#cXdwkLX%cb
zCyTcoz;118YfF_|0Nh{a(|m6D>^h6kv<k@vE~#FJ)<*GQG-w3%&2Zq_BN7n~Qi_O4
zAOp_k5M(-&36Fi^SO2@yf&ip)wBfHOlewZS0{xI)PTO_s@gR_tF011~yn?6{du?-N
z#VXV1DHM^9FAx%wsuLY7KTrzEI<hgnT$$CB9rxOWm-=Gnbk(TI$J?U*&F6w-x00*&
zxyiDP>UpANDzvO98O0*RY-={n%#@v8ir68Dxc-d&_PV2eAASc^nd7-EMW_T-9eGX>
z)jdiFV)^bj{s@<5ISrT8#}_zo>JlLPSaPg!Rlkz&QaEh0azT+s<meC(cwH-}6zXl&
zuXww^5O_8-9z(2exNU_)wO63&t;e-rP%pjEg>g(8`!e5bID<<IWy$c*PtQuPRT#?w
zT%&vZU1ZqWI_N<q_L5-!2awi%Otr07#WYkKqEA21J1W52Ab6R94TV3L5`Ujd_9)Ht
zl8D=89FM&b<EahIW_Y}94bsz*pQ<-Ap=SvGi}aCG{-$pHMTY_l@(QcI@iX|9BEj=v
zuFo5hJVStfPXi{fvLS{>pSzIgKAcmzB?rh%gL0Xz{qJM&)Z+yarz$@s+1?^$sl9G_
zy~3GKxf|-xJKvvDLn4mR%ym)6jnX0IWb>4IQ}w#Nk2T0#IEne6$1F8}6*RELX|1?x
zC<;HC>3Ge(*SXKF)BSlnH|9f&um8!vK2UC|2FiT1mP;}D*3n`=GsB6_y^z@nEADN!
zkLksGR)EvSIc5hM5BkVmJcQ5f87#M1N+|MuN@ZrvC=IHkOfBt_3$xs9cRZbK%W2T^
zCYumCnGraqA1u=kla({WtAF{x^5(hEmUKPuLj|+$hm^#0mwCdoiomN`Xri_`_TWyV
zdyC+;<A=%jgv5)K5QVCsyF0Umnex8wxE9!4SON~eI%HZavb?>CfS<(xU?><3uD$Bp
zw;zB^G?@SKJZ|Ngmx+k7qR08ffC2}tDe#9~+`kFQJOqzvS1k`VG{p!N4$ZL$(q<KY
z+*L};8STh8WNBFZPUp8F74Y?C0`_a<AA0w*X~GQt2+vwc6^0D^qpf0Dj<-U|`|Uh=
z?hn0}MpGjEQCciU`Q0thESHmG-ijW_jRtKVU7nl6ff#W)t;;Ri%<75vM+$7erZGe)
zX3Gtjtknsz;_Qt-ykaH)g~yyY*6`ST&~QLLw|`2)kkuecx_h7Tu=6G>xwQQh?RM6>
zV!{@N!puW-=LKv&1a|%mkj??mWac<u{-X~|@y$xnk~qbWl(u8&jtq!4_dVLfs1EtE
zgKnlDv?~yWXbaOl;uim$3eBMuvdPM(2sLY`m1gtcTQHm6t7r3LUH9-1(*BNekX7z>
zKgVZFY7+_Gyd!&%5I!BGy&eC#LocRe*3cw8N;I9DxhI%jGRmYmOUzNpS7}qR=yFf;
z`O$f0nlJc=8<^*`UK{%s4W$C#|Hs%cP*5zUr|x7{j+kGQqFvjek@o3*w>P;{Q}3D=
z6Ncuu8(zQr4SvL$vw+t(;e7dsSvKj3N*9Y8yeh;|v19W6P@7Ckyi*%1k4LWS?FXEM
z8p!55yL2-7s_7vb>G(g+T#d<@U0Y5{(zSfs7N;Ex+d@cE1Uoq}%bcXQQ?tLB&<e`u
z#DP*DK=jGJ&)2@Qj$BVWzE4s<m!(Ell?qyh?)hFIXG+G<G@#vYuQ6o8SBC!~#_=N|
z&?)LU_etb?e{ldlDlRhG{-REA2?>|sO#%lIviKR=>#Lg&kqm3b^4@~A38ILDgyaKV
zWiZCDF#S|}t@YgW_%lY<MA2_`=}GqZ|7bx!9W7DmiV+ab<zBN0IFsN-s`-+vM{Jy)
zpKL`0b)Tg*`?6Hp`@C)7nc<_NYBsH~T>11=q`)gd6pXgBz{7+JUE*vL%-6eS|FDe{
z#o1#a5*1w&0{**c#)xToKKj}EP*1PKLD6?<J-sV^Jw2+Ba+DMvzwR8LIVPB<rf#?Z
zdAtm2F!fETbmmI^TNi+p0%b_rS$)SyL%W=&Cf(>TsNwz&SUb))@&ug&)J5c&do^&c
z)s3vJ;}@RJMK<^8)CY?ZSTyE;@DQopBf@XzLYQV2s1&y_lI)k5r*SY_=eExj_bhr9
zTFaNvQtXJSI^($!nJZ(49>rzNmzsR$b5D~_!z!QGtMfhbpoyNO_#b!s=>y$a{aWt@
zx|_EGnvWAPrZcJ9k(OzyJqs!R>|XqJXMdhQeW}$X`sAflnXdEvy!n8^GZ1MYk_3~?
zl7uzg@|W7;GYUyT55faW7S-pS%D~Bs!RTL^Ld{$L2&7YX7cx)K?=Yzdl6|+Bst<45
z2QSHZb)s}%8+{S-c+s18BKj=((_qjx`dWmhEjMa%Erl(@d2y8<kyOpv+IkC+3Dq=S
zx538^a^_F^ax>t+K--(?Eaxo2A-kCWYo{wdAaSBGo8~SL-oC{cH*MYfinXH&@6Rht
zY3A3<t<_RUmITEeZKQR|{h#LSY<WpZmrAABWo6|gu22X6ocuWGuJ?33FuLESP&AW(
zZ?KDR-D}I2Me2e*%UNsX6ntGal6Ub9q2Bh|?s7I_fxA{pQFu!o>EW-%?(R>y<wv_R
z%XeqEjfufOS<^3xf7qnjth7qa&LRz2L$kNuJ8Sp?w97ePUYpo`GZx|-E9>Z3qwl)p
z>;YxPh_Ck6#p;C`stqoN$oeM+m2Zcj^PhN-ka>|{8-`7h3XR6H$D@Bg!gzI*D~z1*
z5^e=c+#*V+x9{H7PnBJf*H}cT_+E!*v2e4%MXF$F>L?NJd}q*e`J9xmvfl7&#rNw`
z{*}1<pp83d;%_z`drRqIG?p8QekPkj8cwcT#KK^JbG&WgWY9X(V}$3C))6k$o;A_$
zM+)k;$&^Qfqk+qw6ov(5_G*Dk5CF8l!k{&pU<k<Y)PWXKD#w?p2K3`2lm#@`y3(XW
zIwrhf9?7G4V_+hh06@<gvz-bGcMyoScKv<kYWh<U58>R}Jx+raAf37)kgOKl?#TEA
zL>VJK&QQBZTUDZUDjbp;B)4)E?46e3j8UZKfxb**@ngqs;ot+GqZ#49uJ&F7u&We@
zfNzwT<K)^T-0k|;oA1e3KeV=t5P0`AcI|_V?^Pgb7-9*FPWBlWf0I-ur;ws}=*5t*
zTLGsDf~1dWq@Ss<%Gdd%<k9JKV>M?rKn_CY;!hsDPy?vR0Y5^fhwPUG%@$`f2_dg<
zVbHj!afR6?{oM8Tl?NIDFDR%RL=igo(~M>vNtOw?1$lJR=n^msrP%RUJN(gya6E^5
zCs#RnaA{1;j#JccG{JCwo6)Gyu^=CSHby}Ax<h6l>TqFe1YpH#(Z+80I&0&r9o4Fb
zwC-!)-&xS7{$r$cHxs<IZ{i7otb0FuG+-Q1o*!m&Du9y4)or)h9GWM?ytihRku%>O
zPgVS4wsk=5q&Q8FQwp8`uW^0{^F^6iL`2kJGw^)@A|CQqEsZLkO;iw&FDWtkR{&T~
zbhCP#Jr2o3@OHHsXq#P*UcVi5QvHP7mp)fhH;~s`@SWj<5Yiybg+RbEC1}NM;@pfL
zF8+MWgdp1=`o;a7FWt0JnNX}wn3>aY(H(s90D_``0UlfCj(eQzT)NKLKsUyal6|XA
z+hw`^&=)kKe~tT7$yRt|92JA$R^Mo2#k%*PDVQP9?;OXIstW<st^__b6;HE{O@a<r
zvs2vlU;YYiO=t1X=uUZJ9rJaTY_H+DrikVz0`K$;4J}~Jkn;~wF`@^eG6c3xrO0Ak
z>ut^j5nvRnxB+Qkp8HpULfVCg5cP9sfLlI|W1VT{L~Bvv@_sb{$DN$mc8rHHA^j-6
zJ6tK!Vt`8(LYl&U%W_lp-3lzsRcbw6`C(-hYT^U6rmf9#zqa?<8NBz^iToVk=%I@l
z>5x>Q*W-G_6VKF`*Q6yt+ca^q!RbhUL+1i9{4Oc}0mxW)H%o11pQ$pa4Sis|B(U09
zBi86=*GZQb0l?O=I@&f&PRz`g6-dLKHrta;<8j9BqsB&Nm{o9}ys*Xj4WK;<pNw{G
z6Z;y?bL6d9xlNrSw;UPkn@j`*^Rh#8b(T@i{j^R#_!5ZgWSp0+k9tT5sS9<4&nCjR
zASNfKQG9(76t}2*tal}bB#QkC<s4_hx{{^wsi5f@0VGnTfI)Gv8iap9S+oM_yW4@j
z`=HOgl?f^!^9UDCo}+Y<p&YiJ&ghkJ3}lC9pJ`OIHOqkxQIvpPA{bs)7DzaOo-|pj
z`#s8Q07l}RZ28}wWr6Ms0}D(17LB91*eifpMn>$RbCsbg7G!~j8T;bZaEBl2AxS>?
z-o$?F-{D?DaCN%Q`}@0n&=X%O_!!&8BZuU`{zLbNu`s%mash$nim_WG|6PhKjbM^T
zJ$#B5zur;%vG3L|qr2}ARA3YIwM_1>4gr0FXkMXwZT$~GDVkf#)B&sQmlyLZnSu=P
zm&Z?74N*aCf)sfO<M&^qg+KCI1H=GfdBt2Vc@HZBwBz$PK-GZU%Wg9CiHhCxN`LO=
zfa@&H=z3sv)nzQcC5QBolp}%0t6#?C1y9}9Irun_oB?o<_MROVK<7t$E)%@nLSvi-
zqSsBU)IEfk6@M}KM7F{4akB@~6|I%e1jp4<F`CWAB!ew!x*wQpEO4zWg#nN#&0v(o
z2~81SkQUq-S`t_k;@N+d>O6OUZ0Gtf<Z+2ZfcFG=goeLEvLFIdi_SXKRaIceId=St
zb5RR-rd7pUhibED*Jm87-O12-r&~p^)U3sX(#zmhyuc^OSeW7SMgKC=db5CRnHr2(
za+*pDn*u&UED4pT88vu#`vcC!DfCoio>W6{8bh0kHP9BIA)5obXyN9>nvJb@c^Ds&
zVlH^g*0OlcF>SIzF{2b*JOeFh^T8<Fzo3344KW;7Cc&WpF+%cP-gfUiWF6Kg^*+Gb
zMY-I~w2gaSb>#*TNQO^;v_LsJw%*323WRg;Wx#in83MS{I1I+*zpk)dx2fDbq>IXh
zysQ&}vwAEi;IfPL6;K^mhrnO6U2q*(I7Th8W{X>2r<`6+agd=;Q=^U+KEp?P+P3>w
zIRWpR2uO3qD>_V_#EV4%ym*a_z=ROzi~$ho^U;AreW7>9CpEoqZD1pw;Ud!f39}rF
z{h4q68iAU+zeAwe?D?^dJt-<pSN^Dv`j}_MLyu&b8*gd21D?dPN|pKQkR(y$;fORJ
z_Ar(PMRrQFq-`XB-sWS@d_PCM`+3BVcOS_T5+W8Rj#av72YqYPbn8eGz^7>(HynXw
zcyB<!`G`~8S7c0$2=2LuWZlhKkyecgzFl6v`<TGmU%EJc`S^PGr@r_lLysb3t@K(!
zr18%{_2af{N2F35X;1|4p=i(X$jG0)4o9k2s$B<{TbG5mft6|k!m$zC^Hm+~?Xqh-
zla|+IDM%B|_<wHx&PsA_SY{;CaR~L=$kVu^f?s2P5<vKY@kvmM0oooN?H3r4ZB2Jv
z4x#>YZ+*G8Xwvh(5vnv3iiJDnUN*)tO3l}V#<(|;Iibxn;IrhC)MiT)%H~GaLjoxc
z2?<GJceFNz#<7s5xVLW-5G2q;2|4z;p7^XDtn~CyC^EDhOY!sg={Y{@ftMTtA}j*c
zpU)8KhK1ONe-a1hA`d1=B6md(H-Xctb!IXV!xp8%*j=E==>GzxbKQWSf1DGtd9x9v
zmSe^b2&1M8^|oiR(N7{kYrIvfB$+KfH<>vhcA~Werw<|KFj1W+!Gd_(R*<Vv;mFgx
zCLQR}eC{K({A);vlXXPF`P|h4>s#fo|FtH?CjF?m+W(${Sr~ookMVI9J-Y8<-pdd{
z9V=QQ%PqF74a_LGuaNoiR5evrx(q|6b9~krAyGyw#fVIp7(>&(kyJg($~Mf{kfZ0w
zGGI<3eA9^`iO*X-T4kCclFDnt+h=je#p;kAjEmonQNYidSsc;*#oJT2f%gIK=PLyE
z*&l_4QkA$411+3<JL7@Qyf~=8dSLU74+z&@IP`v8$bo%Y=uR`+YG&G`a^5Mi&#QX!
z<JPJgoh?x76P}Oy>X1$UiboD~D$DLS)y4)4Iz0i1<_hRA?JUPFk4cK|7oZO-#cG#u
z{Jh}IX;V)K-jky6!y3xg{j9Ijmk`(eA8;smC5~D{!25&A+`JF5v<_Ew!7ia#rT*06
zCUe~#%Q9HUmkz8(FAFQFzBQ+VeJTIhFQX?_AaQE`b2aqsg?@z4p3h#N3g5`U*Q!cZ
zyvQ^)K{=o2_0qG&tBikm-p7w0V*=#q_WD@}t`6q7u9l8{@Gn1krRlx&v{}(#SMTQ5
zR9z?L@qKz=_?iafx#UwmDqpO)pKtf}6yq<yL0_w}98-LyWoRf|Ags#aFvGE4=JZFM
z3bPe`sFyUvdp}iHPL8gJVmPeZIeq<;?Y{dD3bMxNI(twCZnEhRR~+Mocx9uO^KPz$
z)no;txl8cPcenJd@i>ReU4Pr{(;3r}PJ85uTA7RtuOz!-QPFR@-r#mzP@jVFBqC_f
zC&FI}vxpq|hJBCr)vmY1AX8|0Ioe)L?Rp%qH+iK@r`Hsed!b0(7Il|1U#@uDP_dGV
zgcRt|XCv&n$^{qNk!CC&;T0WbT*nwj?_AdHA8O_|VzfMoE4FY#1a<pw;B{wztRJk5
z-taQn53sG5^CmQC;Dy#?_J`FBVVbYvg?M`k^`1hXU-t=f7xMOxl#`k|#>@e^rN%0J
zBd>`BXpqAxQxU8}2#~<<@Q+}ZME$#_<mofn9K+YGVu6N@pK_wY8?Jl!*c$-ZgZpit
z3{J?tRV5-JuTpCefArqFDF<HsKj)Q(b|jWUM5CQwD69&3r(gWFbPiKP3lY|gj8QaA
zL>le1=v`y0!1#pDy{Y%c=8l+2@s07FU{5i;N`DSrPgwVtuVJCxZ%ze8--O+L3+)!J
z*ue=ocMxJipA{qas@!1f%2<z~3(~6$78+%AMCn-**0z03D#$kK7~btppc0N?9xBM2
zk<5&K`&M{E1}~(g1FB1_?F`p_{VUxtL10r8KS@+|t&hN^!*8N9G9y~N8x~BvN)&pw
zO{nX)w*c4>EM@((adN;r)<ib>ow?lWMImnsM#r5(EKiT^Qj-SR!r&IKAYCSw13j1L
zW3>-m&ssyxEJXr`4@!?o%`A2T)v`h%FY(4wj}c%wXTTjryAth~LLP309$(a3LN~~?
zrosI~@+aS*Tvlyc(jV)XQE0c-qW4zQSUozn_(Ld%g}5OjCGnx%?({|nrZsz--+A?c
z9B*LY-5>Pz@lpG^>Rwbrx_F0yF@SPW?$1^xlRc~=e1;Id;C^BH0Luh9DX9>jc6Oat
zW5+YXe|#N#a_w}EF@4@X_!LQj9YOYIW4+3Vi&Wnd7|THPV*cI$n}>frTLUAk9r&XV
z>-qiWUbA)Ov4dNQ+qK(_b>&Ls0!0U2$yRaV$(-`Kjz<+OtPsoEcDk?Ym|t9BtS~KG
z=?19g;qnw{(C>>726%h~^6Hi;Jc%>m9A*T9cw0Q_-}XT5lT`PcknQLk@<*v$pP-wN
z{aK-4LSGduU8X>HCUizIM>?3TUBOuW2KHYgF)&=aOOa;-DG{bP?HM@jT5Y!@_CtVr
zx0Us@o3#lBv}VYtUT@pl+D<Mm+7wU+nF-(<IK@5yp8AX|0c<-E>`?`xUi=_@NzuIg
z`l0ht$@eZkmEZFQu#+jCPcWXdgVllZh^FQcP@iF-%XIsm8h{zc)aZ(`DbzYy_Il&{
zcZ<3m`Met7fG}c*gd7*@H1k9L4DQ0h8=y|JL^xr<5Kijp>G;|3`eABjW-r=XPA*(i
z^q@5%DCPk6Y7dB?mJtI7n9nP~h!bd-Qjy(sAk?0qUswD+?IrHA@ys#;nx8*+3(}m%
zE<ihjoG$%SKy7{f5KtntEg%otd<j=(tPPvj%Ju$e;p@QNQOr*zW{))Q$)WdyDC};x
z-DFS7Oc)TD9yU-6=-Yv*QfrzGpsO4A<qN!!hd<3afVa3glrj;2@L{P5RYfOJifv
zv;=KrIR23+p{qYdgfiTI^@#3$p}!j1A8l`^D}880o=&6dC}C^x0kKHs1o_L#akBEY
zN4{bYmJZvJYWWitd|#-NfyggvBfk#+<*u4hez_+g<ff;{w}nH3e!mPcPq+>czx{h~
zkZ^V7vUG3N;7J!`hCz_pK^zY0*v`Wa?VRsMLAxacAdobR`%gsLK-*4~Xq0X~gVo&3
zdcMuN6z1pR{U|Ga)P)uC1^zUo$=o~r%S3a%M#=nq-jHDZUt4b4OG3TK2n-(rXg9}7
za}RTyH`1893Eytmy=5I=Gh3AIMyR;GKO)3WK527k+*yKY6?%DsU|webJ{53XhTw3%
zt~_k>oIPE)ub{s1JLIBu?QUHWdD;*eN^ZNL`3clAF49rOrLRtt+aKNuUluFv$}AB;
zbx2e50#V8>ARQOO=5reE@%n|h0PPn`><{5FM)Jb&A1r~=NL|X{T~N$o0P>e9*+3EM
zC5~5;l^3uNlgielA0vJuIDZd1;OI{4)BYi=>s=M#kzFD6>iek=Qr%tN01vq>hlxTT
zU!B3mY!}|qcrMISJznv+7FR+*(-S&UnpJ>C`FdKm8Sm0;OGuyIGIl_VOD_)MAj}Cx
z|6;fsxu5YQ&y3ekARlun4NpNW&^gcxx?%2oX9HAqRyYZ$eky)I#_m`1%A?((&>p1Q
zJT*JJuQFtVfKJE$KI|Bmg8Di;0QCApx#A~VFVSQ#e?Y|-#~1~;Q0O5oiRGu)R*+<!
z>MvDUJhaTipO%U|8EfflOT%|<J0t)WN?n*!kog0iveSFqsQEXtNDc%JscuFKLW0{F
zm;wa50YVo*ObK9P!XWn=mk5ht-T*cv339AIB@BVdp->_6P!ODcc-IJ%{!Tz&pJch2
zHnKD~3}AV06&qv5r73s5z#Om@_pFTDd#vilr(A=gsYb|S{rKL@##t8Xhg#jFDx4}5
z6{_<*Y;k3;i;r3Eg<PHb-LR>0m1+qwPD0T2Ws0l#Iz%-F78>f1^9%%t$^G}n>-1pK
z!T;=ql0A@)nrM4kbD8-j1^1&5Y6e!j+UzUgdx%T)b`K&v4obw2N%^pt#USJ_eRi~4
zW~6XB?M9FL$=T-b%~oiw`16FgWI}=p2Vkuj&lS(Idc*O5+56t>34J?-s3bQe17@PM
zQ!K6czy(w<^tK{oJ|hzcEn4{#8`?QN3Ud%1ddOu9ytP=hK1_x&`*hG({C_XR<mH?A
z&z#Y!>SaAs+dgxDoks(te#}T_sD23W-mp+sNta|wmwXe)uHg<Qtr8+Cy$`L=9*=#E
zDqnd44zX3*G0^uLkQb{);Pv{Dv|Xfzu%IjQiFcxG9n+4*!qipVWc0XevGTOej=k&K
zcQr&#nukMs-{021AAYqzgdYHr9FMklMn+l8diBHdCT{T_=Oa9IU9tO$gsZp6my^1D
z-!S6@lskX0kK}y=Qp35Thp%>v&}FRSy#wmjehv<r#?yJE_0GNu%gDuok<^rcKHRtt
zaxGN`{GY=2U*F)m)4?Il^mKR-;v2f<Jd8a*M7xCCRzBZV&OfD@VME2jn<j8BPOv#&
zC33(#suI>qvL&94Pq9SuO3q78!<TSFk+Axc%0c=6I>K2Xj#D88=Gr>VF6Ayfpnr5d
z1f=6C67Mm5Z@RH1>ERI&a!XHk_<=#sgd+!pEXEZCqP}ux==&6zU~%k`TvSM7nweyh
zQMk0BvPkhxpsVVfRxnriKsFW1pMT$DhI@HKd%@fzigTk@ilJ~KD#(ogI4{P1%yT){
zd%wFGXJ)+dYTi1(xL8JslxD8WO+a&ElJ-e-?1o-a9^sufyf}r`CE+17zo0k)=ev+_
zcSSSr25D38FkAFN*iSs+aI7XtZqpiCgFsF5GS4oBNf*1}&{*|iG(+HA{O6Aj`o4p?
znxC}r6nv5s1Wo0L6x3+OWM~wBA=hFp=E+2~k9uXBC$J<)Wf-{l%{20c$Xub83OYCO
zlj#yo9ec>fmpVH*h0*=~sp?8^wHX&Kk-FMKcQ-(o%ZHRzx>o(vmJ!66F7sArUzNPN
z<T<eD@!ZHTcvoNO{4n+>{6=O>^V#i3_XiMOc(Mb;5K~{XFStRhKEs$Q##;-u8;=1|
zX{Pk7OEX=bPWjcIM1bl1)|+kjTljg@_#z}^_-(Blv+NORlzcFpK|D8Q)vWPNQqO}^
zqeEbUr|lHY9Vn(=wjjIt-F4x&kpq5L=GC%=(N_V|GFSfz6y`jT!`pq?#sJE>)5uf6
zo73hCrg$|8BSBD11k|xAaHehqWvkINHWx&|$&;kHsj1f2=Vp^mt$+0-`|cX!Y`S_S
z{08Rb4TCsa?CLm+M-VvP&7Y5UBz&IwE3JaX3Vn}gi+#stX@c?!eWgfScR2ZSoT~Uk
z2VBQa25YSA2UvDC{xr1>yeCnjg^~9eXL<%-c<*`(II6}0X<v<KVXu9?l{g5~V)=gg
zT2iY)5q8_4qm*d5!3!39Hb>MX;q~$%D~QfHVEG1l|3-QP0oqLn@CFnZG?gTRS7F8W
z0#I1T#i2d)WGv=>xCiR{D8&;y%ps|^Z^8dOjS6?V$slW1dv2wN>E}uJ3@p=alO>Cv
z5>*nE&ph$@*C}AdUPhruQ10=)s>y;hjIcIv83`L{1)E8L4503dF|Ac;XsR~^y&YZ@
z;?gc};Q}!^&5dz3K;LyH+~4-k53z;NT)H#husws_UFAmxo2##_!>je@$E{@X^IcuF
z)1|CVVFJ{re|=PC^bWJAV*R{6FgskfFZ8qw_-ndrUsPWNL+=Y3`}9Jt)sN9Ke^jBu
z22t2r$bl$6U-NRX7I_I|L~9<FIg7RmJFZf>lRQYCD5d2NFly9Mw1Jh+4{j`-x{rxo
z;5#I`w(c9~t`(J)y;h5N>Xe56k>D>+7T?oRzGw7whF@+BlBwx4V86W|i##k9fY1G4
zYh^WIp&SpD8`P+olS29)2WBkH*&X0eXt<~VoQ2_v1%hUV*L&iaL<9t(it9blKA3$F
zA2V=2z>P>eh^4)FA(SX1A*N0tJrf9b;&a`+GB`dSlPLv-p&TuISHukyr1d6Zk_4Dy
zcm$pY%KOEN4J-KZY2>s34Sjzp26}?xs4GnI0({8kB!n|`1me76wtG-(%$SG+z(Xdl
zq(oNfbMS(~i{OnwvWM(tIx||NknVZwS9l%WX14S=kB^V@@<>_gW%ZFwD8Ff@beO%8
zW!S}`X&%AM=g7W0zM^RAFDb2-piLrmc0>vtIDMzC3m95;?bbig;&6*AT?RW4@j@w5
z_dJLbm<2P18qDyG@wambk)<*-QY8@3qZFCCPnLuDpwQ6JJg@}a90EFg!Jr1M6O#ZT
z0H<?yI3TLaaJz|UR2hH${BlFZ)hZANX^KEzUX7QcloLV_Kur79)x~8GsRm-;<g$W9
zfpU6zdFc_f0j#Sgx3OpT+JoGnvc!gyMGd<C!b<*L-9oZf>2SnKK&5ZRv@xki*t*&v
zdv45B@<;0D&5XLF%U)~&8H@++v6udF7t@XR$x(7r(l?5r7@I(35Oys6JeB_Z>~13|
zC4Rfw6^a1Mis-9Z$w3<dR3W$KgJAdVJDlLIPB7ummln_+`afe^Ghm<my?{8>swoNO
z8}xNmkWs<@B?&1PX%Q4N>0QjW_c>3~EqX^1@L6DqmVbaP0cE&>J`b#~b{Lrt-RK16
z|D07Ba%%BLzYawI4JM=8u%o#i;dOHQ_ms?6RVJTVXctLA)iSS!J1jR2gDlp&bMAoE
zAqa&`zXv<~E!$}wJA^%jX9<j)S37J-3Q<}xWXwnt>>=W)PNhMpksg;e5dk?-=G~zL
zl1Iu7YgXmso%RsP(fv1X3x;qaF**4li+&ad$<OEQNQEY}Ls7$EgI*rIeG$yUL%Y(&
zHBu;i$p9)1$J!37$WI-ar1$KBr`6v$Q-xQ(Q9Hz5Y*W3sIC<L{q(g%93$RL+nDAau
zVYAzI9Y=ySXEr8t^vKqKf7)-uUHsOd_b<v!xJmTm%{PL-<f~@(;?>xYZ5#lV>1&J!
zvj$t?K#?FW8oY~y^HP94TI~2QK!50fm1$YMdq|f*S}uvQ{P-{1)QOnk7c@}f7Z8UD
z1><bg_3O?;WOMW_Xh7ivXo)IiHhG5=BCR}oc%V*v{_760j(*`Qjz>KT!CAjqdU{D>
zb0P#P#Ud1CVkC+=1aNSr#R6$D3+RBWX&QHQm1xSFbu3i)5#{L*-)Ol~o*fn)`mNS*
ziD)wyyw6QX0;Q?H<G+*;loVUOZo5vc3WlMT%8lT#hqn;^>+rO1^jz`_3mIkz>s0y5
zcm@_jL78;*3Rh?<^ez93E%fl;CZi)@WT%2Bfv+NX0@V7%DV^r$p%RQ8EKE4sQ*4~*
zduaR!jnzC9b4cLH5FMZbSb_}79E~p%at9>{<Tc3!SAdA9ZpTusRXn$Ol^<q=DdH5S
z4+WyB0W3Y8GB^2)x5o86osFZQXe-h3=_^8#_x>cDfU}4qixPB_3z~2!o4jPFe@E(u
zTR+(#9sWJ-Gm=Z`_g=#qMDBKPgwKS#t&>EKv8UI>Tr~a&+lxqrjQJVs8gd<}KqIy{
zehI#0ZWNK9Jeh5xO<-XfQ(k7KLA!mIsaycDK4!IagYEtRdltS^J%oG@DL&EJVEu{A
zL(V!DF&_-6@rJ891p>|}g3RzC(Z%d#3lLe>)t0np-1gKKWe*yUKr7Hm(T>211;Q|T
zC<l<M7q9V9G&D#V!=I?@HSDJ!E1)IRNov7~IeLm8AT;^*tEk7t=6WVCMwiff4BLTT
zSa2fVT1Qy-09pdMoLHA7)NI#>7dWpL$t=uyG2FjYzc={h0iXBICzg#~l^%cC+8;C2
zG=g=Nm>xZ8(ob-`Z2*D0(es`A)^|d+0x<*eITL7p2Y6?=W86K<wg_#kI5x<Y62kyL
z!W?9exQRK%q5A@AQk9&Uxsz0!|B6CTEL5CeG|(^V7J)$M2GrBpa6ZFoe$@fX&k)v%
zZcK<f)^K$;Vh0-2c9`*+I6Zet!zbVfyX(jS?VniSunbWH9Es0<(V>PFq*uliFoA~n
zqzq5qLvc(H*$Hfpbvii`&;+p);02k^zL?VEgwidGrkeC17pTz7tw%hP<WNdVyNH3i
zSNt;~;wk1{ITq@{+m3)UlT<MZ^tBJmRnZf04|VItcCh3Fx;r{lWTD;8gTsC+FG_<p
zcmm_3Uk0l1M09kTMdcEJ0vqy{f?}9oWSKJ4BGJ5Lwff3q+=meSD%T!h0DeqUnBw4$
zcp({Aq@RZ_>B+ulF@iuDG%WbvuMxs;e<W!2(_)Exm4Y91R+El{DQ-aujy$IL04nqv
zft^<sCOMsRA-W{nj~vKhgo12&3N!f5Z&W9Zy|`<?5h^~e-J&l40#g+j|NZQXAIf{g
z-t=rizEv^g_{}vH5X8<QuLOMbi;Ko?n3s}le_y$s8MB}=OCV_t14?o*2=GCeoFk%$
zK9D>x&#tUXkrqReV^t<>`5GzJy%GJtp96x2z(nGN%(mYkA>J}D+GCIkMobms1sN4Z
zkUST`&lDc}Om_)RIL*?KRGdT<aSOaC5IsS*%;qNzD#~!Y_loeK>RUyGe4h9jbcDXK
zBFb<ccz6WBfC2w6jz_f2Kcpfq?xoOJFec~Yf6onmVut!n8Vh_9_%%-We_u2R2OUg=
zFfdM@zd6uTu<VhKLB`rGV~E=}JRe87dm4!wVo;Oh-U{Bt{kPVev`N{#Voh8@ZIo;Q
z@dh2=SWUvAE$~*8SO27BR+`wDSRGvD7tmvh>q8PmKpa52=s_Ncy__WByhb{e2%h56
zlzWzR#OYQw9AVKs{hMgPRAEt$nv#QEl`mJ+^E3Z#Bma>t_eO+DlC-75@F_3rY-i2X
zYq=WDrpOUD(!}LiMhYZp12M)_Gsge^Lda?O3Rw(kvlEUcW!4JY^mYzyPRC#IVLk_F
zZF&7jUcFi6j$slHb6aca5>3sb$51EkI$4czf+a*(b-C<TwP5IosA(BrsXqj_AJp#3
zb2*8US?N`^ObxmftDW{5cCFagyGSU@g-_9H=b~o6wd=+02SHQXGV1r=j_edCvN}h8
z6#Di%KMHY>_`el;jfN&`Tx~_rD)cz{N7;(hUjjOp=*x+ak7B!mad;#Vil&UU+QTKi
zu?3HENgr;-cgoy@CMODYX}Oh@pOsO2MEU%$$xVp=PH?o>)`?=NOjTfMWYBU_7zEzD
zU8)vtgQix{Vj;0EizrJdLF#|}j$jaH*w2PGgK<zw>`N+MfcS6Ye<ngyQ{gH$+}*WL
z@W>Kommz0w!kVLhoF;jhxTQPa2Up+7TYMN-)rRMcPlUr>lYYZTU^;1;m?2|dQ|$Fu
zg_LMI@uga;eiajk^WFej<!)ST@jfP}JLDcm9CY}Y-^m%jcNqKBcc*Q#(xYMQL@9`w
z+((zFsdO2{%R_IxP5Y%-$1|VrRi2c)jhp7^dSZ(3*WV`cEd_Ft1win5^S`&ZQ~YKm
zuhw1NyG1=w!q^GA^<le0OdFoL{JnTOcr3@45z58YwUU7zv-3EUVIEdx=+ez^uJc{X
zYx%9xPIoWTNkZIN4S&;6sm)SQzx}t*<L*Cj74sLPsDI5Yw5E&g_zOQAEnZ$dlQ2%1
zarm;x4yU}ir<V`e5qx@b7@pO6bTs_sYgmoLD!-ow$|btqebOq;s{6!sBaKdNc2=kY
zBaWxvSP88R`^$LJH<dU?2_Oe7JV>Cgq*ny*HAbKhA$Gg_CQhNS`=5u-)fCWVDGV3r
z=dB;D2@Xu*)&Gqe4Ru{LsUGbdG_rbg-<bb<t(era(P-Ggtt|J|?nY)bK#9e$o>U^S
zAjc9C0$%-p4k@O%bRVQ({r4{h3I1&~$iF3oB$eQ|Lh%cFAMGXht;6zCeN1K0AvKXW
zV%1xUIPl?c{~h!goWEAwcx&BwrPQxV`-mJIWJ@&lh0?WZG~tJpVUn%aJ>HB=mf=i)
z|IS~`RDxxl@lM?0)xW(I3NuG1!J2G=9HS#DnrDKFf{UX03ANTW?6k&My#xX?SfooE
zM6y&AYm@}^xvxciygr|wBKa;iLf2G4nRhn%#TuT5cZP2J{bkTF?|(5H$t)N1r%n^S
zs0EadRnZ$)??hFSS265YRQve?o5|dALzVx{?Cg_Oi|gL?<^2+@{NBdV>258dK@u1=
z3tQiU8TanLxJD~V5tsK?bniQrinFqs>EAlZUa#_((`;hS`8UH$U#Z(@DdrIWtpPO+
zy~InNhnGF{>P|JoNvPP3SF(-?jEfXWKE+iyDpHFTGSXM#|JF_h2|2dM|9^Mf@A;!h
zW}uQVZEq8)dRbA&kafCqS&^v8#5wT>Y4H8OLqTDfgDIo$|5YEdiMA0n8(x2Dme6a^
zsFhCh^-3ryq$v6r_HNPrP<AQeV*yeHAxwgvM=a&sTY!YfLSF3q7f>qzLp3cR$M-D@
z=KB><tjpiEk&DJ2UBY#k_8>Mqa}*0-cwX`-+oA4hJClx<Y%O`bL@p$IR#v+d4oVu_
ze=iGsR7`{zHp72W{{Mg5AfD`XT9*iU@9q<k79QwD2By)s9?3fDE9#F%D@hKh|GVC$
zed1ZfsibLder{0+dv!Sv^O78`R!p&^n{k|4=LP{G75q}_qLsSV6}G1*myi|iCoi`*
zxp86z2>;!{wJ+&zf(7#0S#f&{w7y+>Al6%px|HoENWTj&+sCvrAo6@uHmHJ|7p^A|
zJNm6Cz#;-QTffQ$X{PiClg&n0Iz_rb-dpY028`|IjOx<frgazg#Iw|t`_1oO%?^B%
zj6J#>tnJs`MZTjMuh{!aDE_~Xy&U5yo-58)JIluXlY>Khys}AU?{G-mHaX(5jvfog
zx~V)q>Z$28b8p$AI6nL5GW<jl|M--yIlYH$r`oI3JMY976<S6i(4lp5l#_F?y~E1j
zBA71tbQDuj0QUSa?jno%Sc?YErGU2cXq!8k%HPDga*ASItH%>w&AZ?dMBGc=Zs`i%
zDeh2_wXm}wGx&e&aTb`0`Kr?lu7AK|x8mcua1#XJ6)#_ReB)jcljJ;86sj5rfTt<e
z_hN*M=G6%8D(}KC-r>Kk4b-SD55}C1eYswm5#;#blXHr@h(^(-M~g}?iX>qWwy@~?
z?KH7Ha-=AF;sVk!NV|lWEvc1V?7*eq`tP#8?UYB1G_DSyl2p9op*{AF`kp}H{Fp65
zT`s)O?=bX*J8@6zdSX8KEguel391RJx@D3Pw5_tz_j(^XE*r|Ll_}j=@9Lh^$>Q>{
zg5ayGqGs5=Vb8lf4Rh9PCXxTmo!rG)5hKYBKcO;i(OfR?%(E`M3DA(83w^UOITYWd
z1yhG&`8_O$@mW+!evXiU_VZ!lHhmK5+GnaK9_N%}PAhB%qLf9FPeoEtg@eo7?0YH(
z48rFzF~tFpSQqnP(iAMoZq-?(`3l?>q`?j;(O9H0%9NcyTq2$FIHPkrCLEp1$PgWM
zBU-hcZf@xDXomzCb3M=%MCVxs4aZI$QflJQq>}|aKD+jRYm8->%2f^+TxL&332h%q
zDSbPj{x1MXk8<$}(nmX|zr=lofRGu3mQUqD7E$t;!5B&N+Hko=J<Aj+GL(pzkn<%I
zJO-ZvE3dcy2V$TOkgj@Vg%JFX13aOY2lTXRaUH^2>4-~c+}P18KXslNqJSLD59@G$
zexL@GQ1d@0Q}=vBtV|Rm+ulsEFe|9ern}Sn7cp>33&j}9?`(pFGPs*jWHn01T1W-G
z6!LZEZ#IwIKF(@8M*Dfgq7w~o9bCQ~;x@pKFV2yE6JWY63}Q)Kyv?5$G1hi~|6Ol~
zZ0=)}`n6;8-Taw8o7y>2M;IHFG4jlks3&D^e^^taOlFG>54p7ZJSgSM@cGok{h&ah
zItw**$B43jZ^Lyn^jBtrUhK&+o=h)8|1TrfpK5r?tTQ_Wa2^dL!_^NQ0c1|0l*Ki3
zW*V-*2={mb<~VS|8z-W^2egF@VJ7jg(Q~Q|7`vTx<doliGxzq!--jOj?-tmE`x58y
zbGdzOk?3Njn78fRG+v$4f&e+OhnzBFWIw+xoK}tczpNXiw1Y27lPV{D3-5TZvz28K
zQ$79*yZ>@oFwFn|rnUd~#-#Rj(2FP68?1ZSCIOkPjN@suRqtD2o#X-3|6cTy>I9|t
z>5*Pds{SvwK|}5T`vb#lElX~E+)@W^jX6>giVQ=tF66OKm6fC2J<Hn>J6dcVT16;@
zIfCEve`{KBY}r1+M>$+om+3IUSbXRUy?rdC%_1z9gENShF8FtMQ(voesjFEh!uI!>
zG20l8xTS|B2`|VlXeZ9G*?Mpwrld_l^3YI}u&HUMl5yVJRa~Y#lW<&U7F2A!i~<?6
zj3&F`p=GsLqt@>?{IRs>ZHi+tv#@#2*(3DW8U>HeN#BcxN|8#PiOdO)ijqW|HMkwj
z=L#xbwj;lnZEt?}eMO}EcG$__V`ai8v017(EcIYYuy~)-S5u6<Cm&>o!YCFwG+BIb
zt?2}3R1Ab>u<Fq_ey{4*->4(q^;7TO{*E!q7+oWEAMD{oyfesLGW5Y7dTA~+$SHSo
zuk=p1u#t85bv!e+>ZeaY#7tb8RGG;@RuyF`wY37NDcLj4n{#{yzah?9bZ<Cw7i1aE
zuaN{JspU0%urJerK+A~5&l_DDi<s|Cs$Sc6k|~?gs(ZKTVNE?A60{KX1d-y_AMZdO
zJ4SR2mJ92J-o@3>kH8<BJo572^;$3sqzBatK&CKkZKd;@!l2-sNb;%n^@pHK+_R5M
zSaW~uk4oZtiD43QS(877sHbs!8}|zuF_4<%j6FNKx8_V3lb^{qPOQDtjvm<j?``c&
z#A~x4u~w^m=S(A^p}&Kl&to%mEGkOw+w-dxkdKO_VC?C&lks$^<H`Etq!wqCA%f6L
zKL!~!TTraSkDIA$?K|!8m2(|nLn|5nFNP$iIHF5D-QQ^<B}V=APS}DgMA$SEAmxhk
zoRuw|Cz%uYKpg1g>l%*K-X5!@VCMZgOH$~^wj?=IJyyic3i0i0DlWt1JqZTlV6ABB
zFDh(Ekh5N6LpcAvUaC%kU)Wle(KLCnwKo*iH6labWAG2##`@(KZ{Kmj=j8dQrBD&~
z0|BMf9VNmqXsvBo^WU!yRnl($-2BB;`0!_mXwB~G$%WWv|2(MQ$kWT_vb;aa;JZ(Q
z9IJr4JgbWN=RwLxqV)y?@BO_h)8}`=sqOcjD<Uojt8(a?&Rt>sRfN8GG^Pj*<Rc$R
zlHDU#I*%(|9uC>by6?&##quJv$V~dQ?)x7m_y~R*BqPcwf}A%o3{p+VaN8`CC$BjE
zT)eFkxIBI5s-O34vF_%u`@S^`LxMjI#0)LND|!1rUb$1gkFpFRTW+`J-Dv!%&d`gW
z?C$zgD`D6JKPC^cOCd`8{7ZeMM@<<BjX9xdQgopl3qqwW&pnEdhqOHJ3~zlAybYor
zF3UeJ$&@-F?!ju;33BzP`Xj?Lp#E3epm4G%_mP2ca{50N^1o1m|Nkabc^&L{SaK~<
zV%)0~S$3?>NB~XvSJ4D*GI~S?vz|6l!+=a4;lGj)@ucH7bB4C|u+<y$rL^Pv$?7WE
z>ZsCxkwTLFSiq;fc(Bk=c_aeF8k})Rta)sbuGA{5i2d*y6z!P*5{J*BezoYegry82
zAekEF#0dKz)i#Z!Zj$MjACA>?Ec6vJE?k2mLGrEfU5+~KgTH_#Z$BQ>!ve=}vQran
ztn*$|6_bD^Z<cKAhmYlP+3rGC2G)l5kT|p<*b3K6W`N6o)Fc+$bR~-vU5;(VnU&Kw
z1o<BSfhnIuKkjOq%AP?4yD+4qlWLjr^6m8pDvRtB_P}v+zyu@<3b{X;pkk$&?N?ne
z#~$r((9Z9aV4~73WuLs{D`$ya^U^F!V9c-mYfb$oJV4yP4sZB1cEtkulX62@G=clX
zShjPd1Tro~kQ-2>0@r;@PQ%<m>~)>&F!{TG`MgYSV(j4MQ(@-Vzno9mGBa5e`>*ZQ
zps3!1xE)k+x4TEFniTmQJt{>|-hXj}Pga8aDy?~&IPgmLH!A%i1xFyx481=QLz;B{
z<b76-m!y<Vz&gf-JbNs3N{GL;k~+@A7x%=v|5My|M>X|z+oB>(1Ox%8KYEqkI|2fM
zPy;Ac5Rl$`S44VEK$?I^Z$arD6{QymJv5OfAiYTu-;Vsg`+fJ_GRA%Hjq&)4k(`{9
zvvc-dbImo^+}F$U$5I_{gCZU3Po9-X%fwRQpfQXV{$=-+pAKZx!xFe^v7qCM>hER2
zSSSO`WCvgurw7gdHq55JYbTVcCG%_=Wqu$+tE~oid988!I0EcrOG(zT0p{^;9xa%`
zRF_X4V`+--oO3My&!&Rm3>))7kGK6#{i*qTrmp7QyjNm>NVQrU^K)_4-0D^TO7$D-
zEYE|9^ob6ZU68t8wkP)sj~AxE8#d7Uig=7Ld5~GyV$y+f+?D%jEw46Zwj1GEKw(dP
zNi|`n-W}RMlomM3itv(T`V&qsGsb_5q2M)e=zqvM0l#DUFVxb%q#u7$D9qX<@Gmj?
z|J_NWJ8!Q9n^oJ5ur;n^I4m7ayQD5GSO_J9uZc;dRwJ3?6M<)t5!jxHRER}{K>5SZ
znN?YKu~|=qpAzN}1w6>7#RNHCj3hjv1;)>&+YVZ-2W0+Engm51C(XACa901&ZkQ2K
z{*b0x@$j9(P{-Sbg>V`aHQ<&pS7Z6B5o`=?smz1fX{~0(;&yqc8%8q)3g83GmuDl$
z6dIC-ZMFXn-scFq{+vzs&j&F{+J7-hxbboqzMNcKy;C<&*ufZ5JD%Y)vIx1t>#x}s
zhq$gTuIOy-p2WnbCI^qJ7L9_SX+>}J^SLET-+>+~(yfT-Zfc__;eTEjVP=P%;&Ax3
zEtPVT{;4(~)4^&nL)OksdU)ATmx*j5vg4v<<WNe>A!twc2}}>^yj{U}k9N*3)uT-6
zBkDoBtlJDa1tsQmv*RGNE@Wf80$li=?J(1Eot#ck`sff<lIEhdhg~Y_y?@9=l(6-I
zjOL=u0qUhMM2Or8ixGy!?uY2ton^E*wD{Q7gWl5%k7NZ&^uTO+QN1CWd!jMqlhE$n
zMm_qe&{V<9Qg@Amxy~+3s)#g<l3*iRJu*z`<3YZ;KdxV=?s34xQj`|$Eek9?3Ut;z
z?%8Me9K|_tbMhCPCw_WuQ`l&jA%N`yA;%MnSc<WiWPKdg6w!san`J5OA0k;$J%v}f
z7R%u`4Yf@5el>XiVVr+BwUCKZCnuOW_9@!M`hmPBrY^I2#mb{RU2cBifPFlm@3y>c
ze40^v?aAvX<7~@b)8$&ze4bL%*Ari)Rh$M|-G&~^wIg}i5;*E@lQ;`LH!153=%ZGQ
z=;wOp4^Oh*%K0Q|?8NoJ0%d7kx#Xnccj-4qu;Iy4x=@biQFibZiI@^wJ2JbW3TXt(
z<n)cQi{`Q6pF3~uhp0CSu5MTGnAT+7^Y1#EFfT>NLRus0cJMcA?}rMTZDpM|E>2U2
zm3XVm_*9P>O!dhx_-ho2j%`li_mJrEM_!?pEqnLj$?vP!rv-x2Y?e)BH*Q+j3xD%<
zgkl^xZac|meO!dR<h|F@yC{F{<8$4ybp*Y~{9oS%Xr`1_agsfq!xFClBK>kz_eD+l
zMxy<}y2MRmmF*(r7$d4_oQo{=tQe=cq<Q!|<0t&t4ShB4AdG$J!3Niv(-Q*xJx4yD
zS@nmUvdo>*^xJWci94Tc-VOf#mV25URh*!2+U}WlOk_~o7i43)mMd-YXRIQ55Uwii
zqid3sIYr|Qr?<31_eNMEwTyCTRSmY{tw|mY!Iup!DDlb%Ab6^n$K*tN&XjcXZG6H5
z<AVbww^9m&Qr(9+-)$JDAa73LPozRD0^~~#*r?iE9Ye@rV^2)RM5B)Rk7VCn4j~$g
zZ%8J$;$1Me{>$NQxIPZSgo9*l$#Ijk6k|GA>;>dIBEoynKNeTUjuy5-uBL!m7n0R(
z8&3?w`H25#@N`-pQNNk0+BWu;o_N4Hw&JXymtHO~RDy|y7jaLXZ@TA1Q>;B7cuVk{
zpOWBaaWcWa;w=vz%LhpeLr3n!#Hz*i1*gz-JrE9g?%rYMuB{~_VBYfacwMh@^`fr0
zN8f28?LdS_h=>-Vd*M8^9R^F6f9ul=^BnDrDk;|@V&V4DvQE4^6pcpi`V6CACAWob
zZ~DH1uZ>!WkJGYUnAF~1B>P-qQeI8~34945N_^XvKQTxgW-rqs#k;w!S4%Z-uk~97
zI%$^4qPDo4E?wZ};LzDenin-pbgSM$vaW==XPQBQ4b%tWIKlXm!TB?o`ySlzdgUrn
zj~+kExkbB0ww6pi|FmX7d}cu-YB+ztr507NJ;E{jQ`M@2#0Nd(0zYyQPC4YvW?9~9
zB!n;ZY~Y2f0DF5K>VOCl#wfS4ygFA~<ON@RLUiN8k+YV5W>U#9KV8erH0;6zoe`U|
zd>i~CqF|2S-i26u$@@d^UH7gFuP6ee-h=XMjVD7$h>@FYJ=ehrd44geJnoY~oc>@`
zJ+CI(+Jc;HRb=S<VNCq%N%>0het*)#^Mx7PKp<#5h(8S$iIn?7pJn`J)2Gt?o!C+6
z^uE-B9^0K<w_cb+i7(d%?CMPKER4#x8!>T<aA>qQBYxd&BpGrmDel*pZn|MymKC|P
zm$2yh%F)xiz8*-ouj7L(kHJjkn?+m%q`hqwg(P?O2^Ad7xH)Nbd(RJg<EKrywH8`t
zo}x!<V!|dtmZAIUE$e2bkiY)MsgzX~eSp`E1a?Eo$_Y27IFAYoTjT8=p!*?=Crnq-
z>{2gSQ<<k~9}=2R`fY1wFS_KVt7pIxBIn)cS&Y+SDuUi!`+bp~;=bp&;ZH8<+@aFB
zAM2pe@SCxyggC?EIDWB9Qge7Mu0j*L?vQBcjm}@iq9O9@o@n|Kz0ksY6`t42IwN{4
z+RtKeRvmml_y>Dcu-V1PwhO&$PpPa`lesDzN93nASuLu6*pmje3l7PfWY>{=*=$Vp
z>0A_1vbY8tuF{lD*0#!dPAWIVgjUbs(9Dcjikx78v;7&Szkd6N1+Fz!55Q*7>DW;H
z%v<N*Smrt=cM6|zMGQa1wri=mLZ3bTu3}gqIEkJ)u!Zn8a>COdCbzivBg~2j`FsJ(
ztRd70_xY!{7W+7j=h=PBg9)=G34Be9Ouu|OK{K_&U|gV1Z|ZsVjO*6TD*F>II?Y0`
zju(X)8}%nhzI*z$+S_`5X*Tl-qwU$gHyt4y_w{0)3U#OBAT?eOe>yldLHISWdKOq=
z;q9BRB=8|p{|Zs&Jv}>HZbA^KHh!u5ToL~S8&-eEN=Z_x1BLoH<V*OIN3dzf5K{=X
z64T@F9#y=~d>=fmde7ij%9BDJrP#u(C;%`@REQu@j=ro<U<x#e3hXcfod2A)h)TW&
z9M_XRe7LvIas05w_njk9&P#(-{oD1rUzjto>B8`+ZNHl~Htk*mK(C~uE(buAYya2y
zs^b~JL}t;^(eB=g7a+CF$C(ZWz>m<xcIb-m=&{%<OZsK{vVh>Cpf{NGhzNaQ;w<GM
z^BdGYBW`Ra_1^*-<_yiKv<RG5l*wrMMHWl^gr<DynozX=lcE4E>uff+&AAooHBT7~
z0AYhoyt9zm_xv?JVpi0WY#820K4g3JSVe5(G=8XZ%O#{eAk%WlNg0YMYbb4_hmUnv
zzio%9!fSm?aMwsZoJKRToe(k=mf0xQIG^6WUoy)A9os^Tepp~2?lRF7%+Cz?C(BQo
zHEJ4h^)LDg9s;Pr_6KT2yH{)_?o9qH9sE#gVCOy|@5HfCtjS_4_h4PAM;(Qtyj@a#
z<F%Tv<NC`^*VIvin(n6FqbLGAPDiGV`(q>}J`S0M(dmw5lbWQ0SGewcx~8mEIMz%>
z&he$%^3WF@;NrL-;(*&Pk2^D1znIqfooC%qbUQb!*vw=8yPt@gB&CJOz<S^x_(Af@
zi1oHTd5*f-xbZPZ^hTD4U;IC@N7|lSpG$Qw-^b1yW|GtQ(QnZA&$r2r@9s{>yVUaJ
za5R4}*p!(Pc>u|DNcTOvY0}3)e^%XmSgzIm{F<h<ZI{5DWso}yWQzH7m|$8On_b(;
z8zbsgRX>vWQtr<-^r2{^z}m=RnL$%cyhXBNel?_tri*+!`lV>n5p+4V|L(5W6oGe1
zX5CJj_ucX%nvLuFpZ3p)e;h+lIMDSh_rCfpFmGq1PCTSiYV9LNJ;aQiv}U^D8zNa~
z)p{FtD{7Dv!}9|2I|Ec`JgXy=$z{@&A2z(3{=p+cJze_xRvWVFk-{m{ZbOy}`#tKh
zG-6=}X^gSbn!xd|XY|!Nnx4+`kX7ZM55MqT;bG8M?Q0?`QRZ`f6mucoNz$6rWDudc
zVN|x})0klYHM5B(PO#$=kF>&0Sh`(aKjOMIr|&?!G2g*PqWLrEBUujX1$F#u_U1>D
z{nFTF=;gL*<bgIDZ~HT_?=hJ%+d_OW>~-9bRu%mTwskRtB&SBUF|qsyT^~amlC`h?
zBd<~eJ`stl<E~d}M!;3(TkF~5jbzww(7gC+&AO!l))oFQy#zX*$m)|dTL;Z>vf%b;
zOU+M{C6=a?#`^~Uz6Y4vx+@QAHNIuJ4;_o+qkR2xTBYkP4X+)dl-q?2s5ne2*G84+
zHo%Gp<<sL=b>}irOQA4}%|1gL;gwiW;l0Qov%zD%L?OfB9&wyq2BTtXweM&m`c4Eu
z6i*E=E_a---|<COqF6)RiW5}&g$`YWX?#=9IC3jovQ!ZS+<Un`4)O(h4KKAq&kefL
zX~J+>^lEj3#-X$!IpW_c7nEEQu+N=_=j>)<W^4MzU0Hfuz8c`{il^XX2Na!u!mHn9
zDT5(Ow0K##!JJOKJ5Rv6r1&s$>RXB|o_L*aH{)Wo>timX(>JTNH-CD{zLA*(2t|}?
zr&fKrsUe^=FGZg*YKXVzo*-)QSWWAFU`R{bZ-x}nug05e?J%=8ttc=oY+80R=(ycF
z+`Q+*L_`=ck_5mAnL;6EwmJSF6~5p{Jl(nFoa`)YG@K0!iJkTSHs{9P@=7i}py3sb
z4UwSVKe5R}h<|o+T20Q_=5e|Oit8GpCM3_wT0H%9NeNzcY=Q&XTeik#j*m{!AJ>E*
zOHtr)`dd}9n@N<AdO{Jj){<yHo*mI*ZceuSi);0w46toIN#Dl$qVvY&!~C47#{}6~
ziMPRXep|MroG8*XJJPdGouTcWA|FA&@_`E15$$BT!$>r!BqkfYlhkD1Xf}oBgZkPB
zb!ss-4`;518QeU=l?c&7LX$rB7w3;hStUasGp`&&um%$f?<M5T>CCs_?BbdeSkl^O
z&X<`uCb$0mb12)s+Oym?$dRuSeDzUs!Qd#n_q}3UORV`Pz;{+Ik+*z!H-Rg#EtX7)
zYyDYVj0(K^h$kBkr(%Q1yiHS*`mIni$oco+s~_k8*7Q4(WyK&tUH`2HZ)U4zzF?Yt
zNf3k`nIEaoWs?Zr1o11HV8poJce@y1g3v-N%N!E}J!~H#)6!g_ADU+a9_y!4(CHt4
zf_my4Mg9J!FJw`fdex|79O55t>DHLw{Bt8#Fi~_(`(|*n^U`cDxaD=?uMM-!i`$6r
zhNCJK1Al4DiJ^{Fts@R^jHx|%YaeI}EHBz^Rr8=wu)bwa+Dv$99XCiUv0G@FR0%?J
zW7*B2?}gi^bl@*rF=#{<(UW@9gfD|KuO)V)c!S-yJ-6k(=5(;Lj+P>+hcKCwrr<UD
z=c?2!NL+-=>OB0cNuYhYoG^Pnge0If%t?MH2H{)uYL89}WLk4J#6z1V<L~)UmSG90
z&psYzXgV#~n1Ek=?Zu5viKnS>5ckRp>kaKik-Kcz@K}<yAIYxjF`RfLSoiEUdu4kl
zZxGh}YMk+2@xQo1<N5eNwIi*-5%~DJ=E^7#q@zVple4$!sQK=g$CQV0;OXufD{PcN
z$dV4L;C}^6t=B)bp>Ytriy<`t!bp6T4;b##H*<;vI8N2HVw4|d3hO9MQ+#f)uHD%&
zW=Y2dQJH`(*yA8{=~7XJp(y^L5x}rD7!KjT#}<f|A`;;*-qTwq(?6d9?r79vS__X6
z!;k(=c>K>8pZ|)>pd-ZnqTOEY)GggBb7cIm(3FDo5>d1on+YbkCwya#sM=%?gAyR0
z|3md4`R}MCCQ^`jWYSf>6H%|E+Qc`>D}*tvSm^3%<mmVpOs&nq(u*J_&Gn7Sl5|?0
ze(}jYqh5}aIX+Fnpz{-E*2~jj9b*GMMV1Cod>*4J+W&E)5a^-iBHbe*J(k77;@|bd
zEehqw1Wa9>H|K6YUY4@E*o~J+p3Tq6OgsmJAyA9~5NUPupDDJ!r#1zWi~kKc{4)z$
z-Lq=+(z?6_4{0&nMp&wQW^|Rt5%TCrwY^;pXS^#)Vs}-<J7$P9oXg0{)Vf)_tJ|Zn
zEbGBxNoD2zR~bB7O5^JF!fHWiN>xP9N+t;@P?ovnzX4ldoQc^lFry5~Hg`+k`Ecge
zcM?33GRKiLSEn3tu<sIx8$O$!a-36~ZJwCns|vZDH8>^%`3KVYo9OT|X^6IL^-54`
zeP8DR<Nd4tT(s<*>F%i`SLG&?lTovLLbhK#IwN2)oB1NJAZ>C#(mlqE!fnMX+8zlP
zo!HwkxIltr^w-p;%u@ls=%I*z1sW#+Xt-YjjrSMG^?<r#^~DSk-KkdZ$HZlE(J#GF
zxu3+<i7#mnXtt^||C;w4579#L{Andj$`Q~h)v)%Q(?JWbicbF$SNkqKNwE0KC_<+r
zjyq_n+yc$G=R1?wbk4>tYyWc2qgwGB-1ZO54CS}9%AZ<pLi)Mc%OU1+5=$^6#!}U0
ztf0?bpdg~FF&n2``Jh2DKcIC=o9ZJ>|I2rL;|EiGjCAzGDBwTr+cHtZiA>T=Q^vQs
zb`y^nlv}m@`-4JWcRBi!WzlG(8gI!`9H`6R&>}}wAr(u95v!vx^47onkClujj0T$f
zf^>OvuupCtsIz`-wvV%%jd{S?_Wi<l+wyrByR6FjNqjN!!#~6a@qZ*fcIQTZO4mw`
z-jc%a)kZML+6B2Y8lFhKR17p#f9dkVRC|YmR%u>;QNF1RonP=qVV&kIr7iJ$KY^hk
zIsDmMzhZl%0*wWRlc6Q_iISTr?X(4nPg`jKS3zMtJA*zqdyVp<xar6;l|7AKV8V&F
zF*EbGce84fZP)YL1szxJ<@2t{9PS{sY+>aTsxrn0G*&bg?gKHfGe4KTGcv)?VA03<
zZe5En(l*Y8ub}-oaTnAc!_L0YNWE6#elaDE6tFg&C42It^VZ8}+NwrD<6erS)Ty>m
z(t=%eMLn~+OM+w#-Y%ZZe`g;$oNbYMmTw}9zh_KmLrOw_hmw{Xpqz`i*tvQ%8vnY$
zZXmmV0L#}1EPL8<?>NY#5y8dxNi56TN9UTC7(`kE9cK7%7$ih%8DNlTG%pXyC&%6B
zoayG$@1|wgqWihpZOBjr^DY5ykCfolV88`VLrn*A6<`jAspd6Tjd~l<D(KB1pNnE<
zHk+;pTRUI*UUl_2GHQPZ*<8oDI25k)*>*L;*o=01C~4`K-6|RcdBQJ!)XbBfQO=7@
zS-OifiK1aIq)cQPdUn*m1Uq%nd-K`NV91dlzlQD*9Ye|jQGVk_Yp7$}WamKPD(mim
zszc_@c@9G=%|cm;r2KQ4R7H58AJqrffCshaY^23PLn8{RWlFdUY$TU7!&GXRl@JU)
z*qpd7g}lCkQ?1$j)l*}_rZ@#BMBd+@`SEv3HPYkL76ezSl~ba6<~IW~&Q;bCCkDn6
z5HMJ9TE9QBtc)0m(@w$w5Vr+ZHeQ?@J%c<qv3PT08keqho;aFI1_j}MlArkKp#Q!K
zP`}4qN6^>Pi`{rdqhX1cO+A}AfniFV{43S?L)fS;$td%%PEM^w<q<$cb#RuGIWKRQ
z&`K}$_2GrzNE-BB+ve90Ex)<KJbrT#NG?krb;z#Yf01VnDatxNzv=p6`J7Vph?{D<
zJj{7BjSPS816qU*H9yCKmL@oyb!blEH)XI7jr8F&;8e@K8SMgd%(^Nt;nf#od3J&x
zt$h~e8%BTwiFIyS_e`)w)IcKaYhsnh-tujW1*m7@l$!YKJsuf5^yzNGU*R)DH6(R5
zj(_n~%fsS5hBOB1c2{0t(TMII^#t9&@5S_iw?yNBc0F$l6t%K6=b2OJ#w%gmrXNao
zb3G;xF5WQ4?eV(>1+zcS37V<tV${M9n1-iV5LioJv4j_#n8w3v&S!fWSHv=)ZSY}k
z2Yo+f4bi*y6RIlhq_Sq(XWc3XRQ>LC+<a)O&(G88dgM^{t7M=3KCGSBFUdM2-(z=k
z=%Q5}rC6&IX3=+g5_F7qW`(Y%mR`w;ug>J8W(k>~o4!rANVz8P(Gt0lL0NRPF9e$$
zC;1llM!?g99E;naJW(T4G+l)WbkOGj8xFK|^mV=^sp;_kycx^(7uHS3@TDuQ1G?32
z8gL1G76c$KSZ7d=gMZw>#gle;a0FpA)_uJ>-@MEU_y!I$ciXMHU;M;B>^NLu^q#H<
zs*0mOk`mWe`-O&*L6^+DG$|-`7%#!qc?A+$I&KjvRQ&_ST=3p8cPS5(!3+E+jH%j@
z-70Na$nDH<Rqq}pi*g&W_xx6Ul@!kBirsNV*c(%SO8j`$Ne-i2egQ3127JEwTdN*Y
zDq31$00b~?sF*#lFvS!zbAUxSJ#CppUc7!5K9^EPBgYTwBa)Z(k^e81sDFn^F6%x2
zo8&I?K$H@qGyKzFgi64&FcB1NMm>A;x00hA@}ZT&Hs7)#``Ni?pwuR98{+8HRx5Ix
zv77$heo=6Cd_c9?CFz5A`aBkVRr^Ety==b~Di4=E`Nm?z>)5!sbmNEA%|WnAE4>=k
zM~`Ck(jp`jLECU<GlFSq-)3DlEb*1i{>p%$xVSC7b>un`zRGlv<wmK<q98=J)+@cA
zyZs8wP;O|Fr6alq5-+iG=j5PB&M6!ks#;WYT$TXse^8)cH0~8i7#62^Q1wv8#K0wb
z%g^rR3Uz8WU%f}hm;|2U<Di?y+fWye1EXP&A0W!aTeb{e8?}r7B5xNRKRzk+<K;a5
z(4C~gLIZqYJ5Pzg^~zyN5tSqzEmv@ytaSOjwo&Hb+nQiZpJ(k~?b-B;9(?H)Oh;8Y
zD7Im$aN$r!fz9ZtMDbd=_dGL1D5XWc>6H=<kzM~^_;!M`M;npYegokySV-zeZK_J$
z{qonDyTrdmGGm_4Bx9FWeQv6bj*cf#>OX0&`(t7Pe}fv-Gi9VPb%*k{LBl)ajPj&7
zZTb9xA14?Ip|eN>{vbXkj<b6rN;(^nfGY3+o$HIMwxlL71V>JV;FqU6uk}F3d)Le{
zu75wVJl9Zz*?P*Jeq44tzKj-EMO$5yGN@(TH}*DZ2ZC5lH#W%jVlH&TG1ZxBJIOKf
zSIaF0_q0mj1=>_02fTKLimc>)Yq7_RpW2g-7&O<);iFl_m^K#CC2zOBxH|(8w%omp
z*t%ao+o-Z!2K}xJo!@-#B9a?;LdPJ4OCC$R^wQWlii-+ZNDMC}6Vd<?<!(aU79)P2
zJO>c=G51eU!xKfS7=!KJ9$-Hz(93LAb8wR@0&2*t&gTIj(z+lhgphR((9`m38gBxz
z5>8^L>Qz^%vhHXZ52HjcZVAzxix`*$<>{%&xl;6Ofm^`@Y>GSf*z;K+ViT+3_2>*r
zZA&+TzJ^2D<XEssJlfVGz^g1|C>0Cn(t;Xqj$g1iq|&LDWbL^JC2Sri*ZraZcY$?I
zlz2CkKso*~Cl#jtI`Tm9OX8u6UUb;K`|3Hnx%fVl)Z*>fS+WYi99-y#)>Xnrm!)~d
zYAbzEs8b-lCVNohD6kgwV#hVlbCFrt0xyRI-Y4JFu-$fcAd9V1^J^r+_>7E(K3S3n
z%hKUOP6$F+QU^0bd;N*hVkt*6k2~A1Je|J+mjTnuf~^8GlZ^Gtetrvg0C_rV{&AqL
zAzdQ95VdG|4|_<Fo2JwApVu~<x~_juN=}nzZGIwoQj^x-D2kDnw<uqlbB~R3bFCNX
zIJ$8dvsc%I<tM77)qfyl5N3d!lHzmvB3t%)i{%4HbyU_w4{+sv-V^@u@dn@Mnvt<F
zdzgipQs5-8H@~pGbF|puO0@MXz|f+>JMhll)2LZoT331K<lb`Mp)5+V=VA4exVEm*
zZ{rtGLYGQHmFr)TBVhYJ=~{4qtvxmK2#JYjjwd)+!MLgvm^twb{>+a`TM)H294evP
zP$hGEt7B!uS$I?SX6DF+By$V0U2*(#8m~msphPW-yqx;4i_gC-{cYf;LRkSu`MyXI
zY?)k}ZjcABw1dol_`J(ZPL%hGz%eAd>&|}N8%Z580a@oTk_X7vBO?#3rP>KI#I#Gh
zY1_^VzBERr-@s&}tmEplr&NOU-V>%i>5NS6zkh9eJq-EXFrNc_K(sRjr#cdGXtu0}
zr>06q+IBl>o<w#77yOFpeL5-cG)Cn~%qEw{b&djxO3uI#%)^Dt%^^hB#fNU1b3$~i
z?+`ZBA#V3-BjW*o^*q-5ZLA))=*}-=+F0+e;Y#xxniu;Wu8)EGb!gOFT8zci_m`k6
zQ^4x<DQ*(I-5pCyjB)<0Sabg3<J7x`Oke}q%B2Xo^IT6KU=c;8DSi(aALo3^@2Fp@
zn|4NUBO4Shz*@P+&pnH2Qca3v^f%Dc>mCX|5<2<BF3UJ1_q&BiY7=Y)ojHGA7`Vjz
zR}>e4mzS42rZ@g%HHXio{Z}1PGw4b4%18)4$g6UgK&HK|9+H(>FFgnDQdQ-q%?8_u
z^f0jU%Y7Q!uPV3JeL<j1`{mpt<?0D%HA%1u(03$Q6UD@{1a=ry7+H6M_9M)}R%#z!
zN9oa$psIstsHsuR6j*Xi<eE<ipyi7ZpcBaJeNA-l{ko4XSd6=vh_aJ(Pm3b?hV4tB
zN!X+0piE<khs=RyIC0Q*q&g028C_4)DZ&4EoU~1#8C{4t*5oT6(27Xu@w*cSntXj0
z)_a{X>^6?;*Im%Q3~L0=FNEMN5<&zh5O5Mw@R>o0pMfR)k97BsUgCd1A1Dt$e0KLr
z>*LP^t>IzU$CJdt*5CgWRd{WTf9~#kNO48bqZFtv{$>f>*iEnJnM3oxE55>9E-bMG
z7!$?<QyCA?W`R+D!0|i>bAV0TijMv5*r?I3UtiCFwpNU(b=oDt5tiutlJ61kz`#J2
x|DpY*dIZz;1wIVLY=L?yANfC1!!Rmc$Q=;PH}sY6Qec5U$bC&Eq=I?q{{l&cxsU(=
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 3295661..9fa4031 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 0/9] Dynamic memzones
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
` (8 preceding siblings ...)
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (8 more replies)
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
10 siblings, 9 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v5:
- Fix rte_memzone_free
- Improve rte_memzone_free unit test
v4:
- Rebase and fix couple of merge issues
v3:
- Create dummy librte_malloc
- Add deprecation notice
- Rework some of the code
- Doc update
- checkpatch
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
Sergio Gonzalez Monroy (9):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove free_memseg and references to it
eal: new rte_memzone_free
app/test: update unit test with rte_memzone_free
doc: announce ABI change of librte_malloc
doc: update malloc documentation
MAINTAINERS | 9 +-
app/test/test_malloc.c | 86 ----
app/test/test_memzone.c | 454 ++++------------------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 -----------
doc/guides/prog_guide/overview.rst | 11 +-
doc/guides/rel_notes/abi.rst | 1 +
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 340 +++++++---------
lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 ++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 +++++++++
lib/librte_eal/common/malloc_heap.c | 206 ++++++++++
lib/librte_eal/common/malloc_heap.h | 70 ++++
lib/librte_eal/common/rte_malloc.c | 259 ++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ---------------
lib/librte_malloc/malloc_elem.h | 190 ---------
lib/librte_malloc/malloc_heap.c | 208 ----------
lib/librte_malloc/malloc_heap.h | 70 ----
lib/librte_malloc/rte_malloc.c | 228 +----------
lib/librte_malloc/rte_malloc.h | 342 ----------------
lib/librte_malloc/rte_malloc_version.map | 16 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
56 files changed, 1929 insertions(+), 2354 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 1/9] eal: move librte_malloc to eal/common
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (7 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
Move malloc inside eal.
Create a dummy malloc library to avoid breaking applications that have
librte_malloc in their DT_NEEDED entries.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing to free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
MAINTAINERS | 9 +-
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 208 ++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 208 --------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 228 +---------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 16 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
43 files changed, 1445 insertions(+), 1423 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 54f0973..bb08e0a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -73,6 +73,7 @@ F: lib/librte_eal/common/*
F: lib/librte_eal/common/include/*
F: lib/librte_eal/common/include/generic/
F: doc/guides/prog_guide/env_abstraction_layer.rst
+F: doc/guides/prog_guide/malloc_lib.rst
F: app/test/test_alarm.c
F: app/test/test_atomic.c
F: app/test/test_byteorder.c
@@ -97,6 +98,8 @@ F: app/test/test_spinlock.c
F: app/test/test_string_fns.c
F: app/test/test_tailq.c
F: app/test/test_version.c
+F: app/test/test_malloc.c
+F: app/test/test_func_reentrancy.c
Secondary process
K: RTE_PROC_
@@ -155,12 +158,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
Core Libraries
--------------
-Dynamic memory
-F: lib/librte_malloc/
-F: doc/guides/prog_guide/malloc_lib.rst
-F: app/test/test_malloc.c
-F: app/test/test_func_reentrancy.c
-
Memory pool
M: Olivier Matz <olivier.matz@6wind.com>
F: lib/librte_mempool/
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 464250b..2b0c877 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -102,6 +102,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -300,13 +302,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index aae22f4..fc6dc2e 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -105,6 +105,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -307,13 +309,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 97b364a..d45aa9d 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..2055539 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
+DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index c73ffb6..0cc9f8a 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -78,6 +77,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 67b6a6c..0401be2 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -47,6 +49,7 @@ DPDK_2.0 {
rte_eal_tailq_register;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -62,6 +65,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -75,6 +85,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -88,6 +99,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 38772d4..0c43d6a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..8861d27
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,208 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index e99d7a3..9e66442 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -90,6 +89,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 3696cb1..57454e6 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -52,6 +52,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_jhash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
index 947e41c..32d86b9 100644
--- a/lib/librte_malloc/Makefile
+++ b/lib/librte_malloc/Makefile
@@ -28,7 +28,6 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
include $(RTE_SDK)/mk/rte.vars.mk
# library name
@@ -41,10 +40,7 @@ CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
EXPORT_MAP := rte_malloc_version.map
# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
+SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c
# this lib needs eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index 8861d27..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
index c313a57..4b9dc7f 100644
--- a/lib/librte_malloc/rte_malloc.c
+++ b/lib/librte_malloc/rte_malloc.c
@@ -31,230 +31,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
+/* Empty file to be able to create a dummy library for deprecation policy */
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
index af6ae9b..63cb5fc 100644
--- a/lib/librte_malloc/rte_malloc_version.map
+++ b/lib/librte_malloc/rte_malloc_version.map
@@ -1,19 +1,3 @@
DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
local: *;
};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 2/9] eal: memzone allocated by malloc
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (6 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically
contiguous hugepages, memzones are slices of memsegs and malloc further
slices memzones into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 274 ++++++----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/malloc_elem.c | 68 ++++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 140 ++++++-----
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
8 files changed, 197 insertions(+), 317 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index aee184a..943012b 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,15 +50,15 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
const struct rte_mem_config *mcfg;
+ const struct rte_memzone *mz;
unsigned i = 0;
/* get pointer to global configuration */
@@ -68,8 +68,9 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ mz = &mcfg->memzone[i];
+ if (mz->addr != NULL && !strncmp(name, mz->name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
@@ -88,39 +89,45 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
len, socket_id, flags, RTE_CACHE_LINE_SIZE);
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* Find the heap with the greatest free block size */
+static void
+find_heap_max_free_elem(int *s, size_t *len, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > *len) {
+ *len = stats.greatest_free_size;
+ *s = i;
+ }
+ }
+ *len -= (MALLOC_ELEM_OVERHEAD + align);
+}
- while (addr_offset + len < ms->len) {
+/* Find a heap that can allocate the requested size */
+static void
+find_heap_suitable(int *s, size_t len, unsigned align)
+{
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size >= len + MALLOC_ELEM_OVERHEAD + align) {
+ *s = i;
+ break;
+ }
}
-
- return addr_offset;
}
static const struct rte_memzone *
@@ -128,13 +135,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -166,7 +167,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -180,129 +180,50 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = 0;
+ }
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- /* check flags for hugepage sizes */
- if ((flags & RTE_MEMZONE_2MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_1G)
- continue;
- if ((flags & RTE_MEMZONE_1GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_2M)
- continue;
- if ((flags & RTE_MEMZONE_16MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16G)
- continue;
- if ((flags & RTE_MEMZONE_16GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16M)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY) {
+ if (requested_len == 0)
+ find_heap_max_free_elem(&socket_id, &requested_len, align);
+ else
+ find_heap_suitable(&socket_id, requested_len, align);
+
+ if (socket_id == SOCKET_ID_ANY) {
+ rte_errno = ENOMEM;
+ return NULL;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
- /*
- * If RTE_MEMZONE_SIZE_HINT_ONLY flag is specified,
- * try allocating again without the size parameter otherwise -fail.
- */
- if ((flags & RTE_MEMZONE_SIZE_HINT_ONLY) &&
- ((flags & RTE_MEMZONE_1GB) || (flags & RTE_MEMZONE_2MB)
- || (flags & RTE_MEMZONE_16MB) || (flags & RTE_MEMZONE_16GB)))
- return memzone_reserve_aligned_thread_unsafe(name,
- len, socket_id, 0, align, bound);
-
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0 ? elem->size : requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -419,45 +340,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -465,14 +347,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -485,33 +363,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index 8861d27..f5fff96 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,104 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, size_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned ret = 1;
+
+ if ((flags & RTE_MEMZONE_2MB) && hugepage_sz == RTE_PGSIZE_1G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_1GB) && hugepage_sz == RTE_PGSIZE_2M)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16MB) && hugepage_sz == RTE_PGSIZE_16G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16GB) && hugepage_sz == RTE_PGSIZE_16M)
+ ret = 0;
+
+ return ret;
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -206,3 +186,21 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
socket_stats->alloc_count = heap->alloc_count;
return 0;
}
+
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 3/9] app/test: update malloc/memzone unit tests
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (5 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 440 ++++--------------------------------------------
2 files changed, 35 insertions(+), 491 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..6934eee 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
-
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
+ maxlen = find_max_block_free_size(align);
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -762,282 +680,6 @@ test_memzone_bounded(void)
if ((rc = check_memzone_bounded("bounded_1K_MAX", 0, 64, 1024)) != 0)
return (rc);
- return (0);
-}
-
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
-
return 0;
}
@@ -1125,14 +767,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +783,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
` (4 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
During initializaio malloc sets all available memory as part of the heaps.
CONFIG_RTE_MALLOC_MEMZONE_SIZE was used to specify the default memory
block size to expand the heap. The option is not used/relevant anymore,
so we remove it.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 2b0c877..a54957d 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -103,7 +103,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index fc6dc2e..72611c9 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -106,7 +106,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 5/9] eal: remove free_memseg and references to it
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (3 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
Remove free_memseg field from internal mem config structure as it is
not used anymore.
Also remove code in ivshmem that was setting up free_memseg on init.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/include/rte_eal_memconfig.h | 3 ---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
2 files changed, 12 deletions(-)
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..7de906b 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,9 +73,6 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors - NOT USED */
- struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
-
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
/* Heaps of Malloc per socket */
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 6/9] eal: new rte_memzone_free
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
` (2 subsequent siblings)
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 ++
lib/librte_eal/common/eal_common_memzone.c | 68 ++++++++++++++++++++++-
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 ++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 8 +--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 ++
6 files changed, 93 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 0401be2..7110816 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -105,3 +105,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 943012b..4141a7f 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -77,6 +77,23 @@ memzone_lookup_thread_unsafe(const char *name)
return NULL;
}
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr == NULL)
+ return &mcfg->memzone[i];
+ }
+
+ return NULL;
+}
+
/*
* Return a pointer to a correctly filled memzone descriptor. If the
* allocation cannot be done, return NULL.
@@ -141,7 +158,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -215,7 +232,16 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+
+ if (mz == NULL) {
+ RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
+ "in config!\n", __func__);
+ rte_Errno = ENOSPC;
+ return NULL;
+ }
+
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -291,6 +317,42 @@ rte_memzone_reserve_bounded(const char *name, size_t len,
return mz;
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_write_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ if (addr == NULL)
+ ret = -EINVAL;
+ else if (mcfg->memzone_cnt == 0) {
+ rte_panic("%s(): memzone address not NULL but memzone_cnt is 0!\n",
+ __func__);
+ } else {
+ memseg(&memzone[idx], 0, sizeof(memzone[idx])
+ mcfg->memzone[idx].addr = NULL;
+ mcfg->memzone_cnt--;
+ }
+
+ rte_rwlock_write_unlock(&mcfg->mlock);
+
+ rte_free(addr);
+
+ return ret;
+}
/*
* Lookup for the memzone identified by the given name
@@ -364,7 +426,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index ee62680..5223b1e 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -240,6 +240,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = mcfg->memzone_cnt;
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < mcfg->memzone_cnt; i++) {
mz = &mcfg->memzone[i];
/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 7/9] app/test: update unit test with rte_memzone_free
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
Update memzone unit test for the new rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 6934eee..30eba8d 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -684,6 +684,82 @@ test_memzone_bounded(void)
}
static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[RTE_MAX_MEMZONE];
+ int i;
+ char name[20];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "tempzone%u", i);
+ mz[i] = rte_memzone_reserve(name, 1, SOCKET_ID_ANY, 0);
+ } while (mz[i++] != NULL);
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ mz[0] = rte_memzone_reserve("tempzone0new", 0, SOCKET_ID_ANY, 0);
+
+ if (mz[0] == NULL) {
+ printf("Fail to create memzone - tempzone0new - when MAX memzones were "
+ "created and one was free\n");
+ return -1;
+ }
+
+ for (i = i - 2; i >= 0; i--) {
+ if (rte_memzone_free(mz[i])) {
+ printf("Fail memzone free - tempzone%d\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
test_memzone(void)
{
const struct rte_memzone *memzone1;
@@ -791,6 +867,10 @@ test_memzone(void)
if (test_memzone_reserve_max_aligned() < 0)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
return 0;
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 8/9] doc: announce ABI change of librte_malloc
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
Announce the creation of dummy malloc library for 2.1 and removal of
such library, now integrated in librte_eal, for 2.2 release.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/rel_notes/abi.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/guides/rel_notes/abi.rst b/doc/guides/rel_notes/abi.rst
index f00a6ee..2aaf900 100644
--- a/doc/guides/rel_notes/abi.rst
+++ b/doc/guides/rel_notes/abi.rst
@@ -38,3 +38,4 @@ Examples of Deprecation Notices
Deprecation Notices
-------------------
+* librte_malloc library has been integrated into librte_eal. The 2.1 release creates a dummy/empty malloc library to fulfill binaries with dynamic linking dependencies on librte_malloc.so. Such dummy library will not be created from release 2.2 so binaries will need to be rebuilt.
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v5 9/9] doc: update malloc documentation
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
@ 2015-06-26 11:32 ` Sergio Gonzalez Monroy
8 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 11:32 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/prog_guide/env_abstraction_layer.rst | 220 +++++++++++++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------------------
doc/guides/prog_guide/overview.rst | 11 +-
5 files changed, 221 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..f3506081c91feb7b80b0389c80e06addafcfb907 100644
GIT binary patch
literal 80952
zcmY(q1zeQf7d46^AT7<1$|x-@9Z~~Gw@3>k4N}q|9S+^yjg%-1-7z2?($d`^4fh$|
z|GnRL{UI<sGiT0o&OUpsz1E&k6(t#5ED9_X6ck)JSt&IX6jX8)l*j5%(Sg5wi?8$n
zemrtilaW9vAEep>9y~D<R}@D<sfxtDF-8L(V}6#^aYRAE>qP#46wiW3iGp&UFDE6g
z;cBp(j`5XPdh#o+HT}j*@<$|`EX+w9{$WWR-!kfx_FK6Zhd`VjSOr7_I-JHRQX<?*
z;1H~kr?P{U(faEZ2Z~c%tPTAGm6bx=2V<XpT{{<*mz0-ipKWWVM@iyzaQydG;#=QV
zVN4$M@YM}5!TRqv;QK@n)BpY)WUN71VD$e!P_;?&BY4%-)#WgusV$c-=*&DfIy&m~
zO(r2DBjXv@FU8lTy0!cCu}3@i|5C;HNI^b5EGI{lZjFs)H^1P}fcu&;C2WW0hc|>+
zCy^C1UM_!QH*LB77yEB-R=!Xvy~Y>aYO-u(u{)enUP~*1^?l{rb8zvy$r=mw_}{5~
z?EKXUpMIwlTkp@o+oFFy?;jW_JFH*-ohlE3Kz{a{yK0@{1)xp02V(n!t(I=hdm#w^
z+L{^-Xy?ju*S9l%AFytd`_FEW54%ovo<jFEDJf|QP6E4Oc}s8gj~}nZ#C$|R=SA8T
z8D)BKH40wqqMi%Ns5j=rZ->^X{?fcn67bm?&5wzV&F#U$!a7alw;at2Z6+E*Ye$Rb
zYVY6Zx<<L~BsuDLTcuupB)dWS`a`dw&u#wFvS4PdEBaAnnQ#E^^zQTzU~=M<)lm6l
z0mo0DO<(uAE|lr~@B<q^-7(AbqQ{_iit;=ik!e$X*eTd1CmQ%2y<*Bs5%<%mmfJl$
zhl8ebbQxSQ{B7~p9Q;R(?fi%CNLpH|HxK(7Lc<*$M(EzRHNnQrj1@r+IbM<pA(!U-
z?;?geN6r&<QVPRAMU;?g37Y$(9@_qa`u`&0V;3=uk%@_E1VRP|1x1&QR~Q;bh+dgf
zf9L++tF2zkU}B+<X<8$~q6awqj?ShG6BRl5{<}&7jGd?7@TGbsLZ~Fin7j{`{Fnk6
zR8^eFxT$-T&TpSnPxc08iZ)iXSG%}C{NLLycLQ(R#+&X;Fd&7H{q*@0htHiScQhCB
zD%e(`Erf9H%V>iYE;~5+MdS~Z9C-1=oq`RJm)yU6`}Xa(Q>UzDlFv~GzF1P~hlgc*
z7}VH-FBN!pOtSmgb{}UCm5Hq-F7j#!CGti@>K8P>8^2^J)3~2^S=<Uq|1U6wA0}J8
zH=6$q`C1C%O+UZKl*JEA0Jbe70q&$YoyQ{P;o|Z6msuM#$MFAFfD%}NmkbOtdt<oZ
z<Tf75rS`z}*T0eH4OWNT#iY}B2j$ZRlzljtjczCFynVC(=i26oG@N6M0C=4uur}VM
zY{?H(<ydY5RtLC|aJJMN%a`0qk7zu0t9bqXo9o<rD}i?^Y%%SMdo{+Rxq~Pm%3WKY
z39pu8sRbW(5i9i;$;ZB_A4q4o)!+WNEXXaM{`l?nfa3S))2z1}Yir3&Sr3ERn_xlW
zUgo6ESai6XOGbb1skGSdeGeM8h*F3m7)_<|aNqa9iFzc$&+sR5x3A)}m}iTivwLr)
z#o9hu8fkYQKkt`TLF4yg3XgW?8X|B)KJgo;TOmYznkvzIN{z-*e7r9=PS^~zCLHN1
zuv_jcL(Zu=cvBKfbhx;=p{X{%qHAhu{)YT_U)IPyz}zU9eB*hIzs+nS@d8tknwe{p
zZEAJzRmc3|{HrNy+Wmi2M34}iFB=!P?=V@2^m{&+&vrY-i{BRZ#;+aJ;wc@GPw;s*
z2W7`Pr3mwX%;ad}0ZsbsH5KCRv4Mkd9A!#B&L1jJ3(C2lA5LiyPvXuGbx9ifc-wZK
z=>Ks)O^}l;MCS^19Smw0kwBMjJjh$f^*jIG#351Zdi3%C7oYA<peEU*vqyqo63WYc
zg~CW}dVIN>)zIj)ue84F1SIoH(>v2LYhO8f;3f`V_>OY^`JS}THEx)-#MB}tS72_{
zn2SS28?$8K<RrEwYFw>=d!VHNKXjtvfA7EmiyN6^FRz4jN*1zNWs2F<z|x{v{4vnA
z&$k=?2~gs=tX|{AwZX;R$3OOND5moMlv{Yn<elM6<eX9Oo9dfP`9+SJP*2i}tRqT{
zwoQ|t-N1{<cNF8scP&xN$TE~h^F0pDeg=~eRX(?NcITd~!s85VNa%QZaL|0Mq*Y-+
zP4&O*fs4!@V+6kf9Mt89MAikeIb(!Af`2=1PT=2_dAB=K1vz03ME!5rVy@}h{Zf&#
zq_2U$v)X4}G4ahi3HL3yplXL^uhF?%O(F!kA^%}f2=d?rp!IIf=f6mPx@l@DX6f6f
za#2yD(_`KDCi5U{Gg^k%IBIU~Cvp0mp7%$WH1mtelo#Jm<`9fz85SKuy&?OztLC<<
z>Bf7mHK#kG5<wR_%hBgEMs*CmS=A^(ir;g%WJ<+9_XyiBOPwTqLLwSu4jE*7v$cuo
zUuKwIP#E-MCCYt!blyKK*VIuUB!nL`Xg5!g;lD&N&_ly7;l7ckJNb9eal;Y!lcqM;
zxx1N*q7pS|!Ubj}>T&5UDDFu}udUQA(hvb+Yhtc59C&g<)Ch|4v7HEDD6?{%dy}3t
zY`{D8Q6E$wPoWjxg>}N(EjuNi?&lF6=dJGVVZ$+};6qDjufNFOQF&<;L11fXBQE#f
zq0LufRIOjNN~&nSdiQVEP7Hpr{B7;-`T##-Qpr2%++UA(pUdqwZp!)oA~9lW@pskY
z%ZAW98za#-QuPXF>l&fr-rmhru77>E-|USki{VB*q-A5NNG5XC<CRFU5<mL|w~YyR
zG}OkJN9Qzd6?e7~w;zg4jV3jwpX}~SLbuUH6uKloGLy4tm&eV2nN^&ru>i01MB@M7
zBXPMMn`&|l3ZE#r9kyZk=l-OO!};_5x9wE}{5-r9^`t06#AEwucMq)()&GZ8$bHvr
z#OMEuB5->;Mg(c&fW0@4ZUUgQh)cw{9ldad@*$YZf`6~V2<cm@dA}-p0}1`=Y$x@9
znVuI5Nak|l;=W2>0cb(z`zdyR^8-lE{}+6Wf7zRv(BkKfp<t!g{{8_9cK+W9`AL^!
zI+!LPI5Mjs12YY}na`HQ&~JEHbQ~}ZfG;sH7><pnPDj7&rzEQy>uIjr|Do&ykgm~j
zbB|FNK-By#G)~rk+vE-YpL}laT9vGA2u@~VWsR$7xtm$103qL$fUm$v3e&*(55S!K
zz(LWTe+&L?=0^VVwEaIZxn3|Si$)H=0MIqRriO?A;{yRB0GOCmC~#wHj~vylkbmm#
z{`ftF5x@)fmYa{UhT(bWV@0oP=hT`C9^T5h(;wJhzmMV-<w&doX&rxRN(5l1Pw1c#
zJXWYYwX*1a{k`#ezv1|DBeVbJ^5pE#LCbylwV~IUX0ny;Kxy;EGWO+hKRa{{5%0c`
zBmK?k$A}nmb!2h30|s8JX(*)d$o-plG2B{gX%W8o>kn`iSvK<GS)1B<Qvm$x7i;Yd
zFQ;1W@-J!Dvh7#89oo>ydYKB-=-_ZT7ZgEEy~lrdv9cB;&lY_$BxYEkm|B2!iOV<l
zykWn-bk1ebYx}t`+D)BdDzA;{T%En(hnV>IS2EPjajUDVy_+Ms6H=uXV+9VK1TwN6
zLAYlIyDNfI4_naL3gBF}w#)tPd7`?qh$fBuX7=AA<CkPV%!U#M(}haaQM0mrF4q!E
zmpQB^Sw$urT<kRIfv93|b8~awJ?y|w>}P9GfM$GSZ+`!4)>a6<T~dmm56?}@Vx9B8
z-mAy&O<oKjL_OP{h~3EaF_3u&z%r-BL`i&&&8${OC4dl@7b}s@JodrVU!r~t3d*Om
zb4ZNlNMkomK1?tL3B<-vay`!XXP(yI-(CCX21V-;laLfIxAu1aZS()RG{o5HanQto
zP0Bj#sPgv9j0J<u=GfmJ>(7~9|3=X7bnTk*5l`0DJlVhN)+TV8boa)*l0W?r=d{YW
z#r<hu_~h>3{!V$VFw@(8S|R}mk!n%L&Jcone8yLcUgzmBDgm`~(DYjZaV82;&pPH8
zu*x}y{?}Z_?N7Ye5PkMN)C?F@pTDN~*x&E&S~o2`%v$jwq7((_)e;Tdi<!tFqwS5M
z0D02?_2(nD=5t;WVY99<l9|6fPGJI6Vz;NUDY8|2RXsG!zzrTul#<YEiFo|e$X{9t
zrcD5XPjTj_6$5+IX$<oVM)I%I6O?w1+3UJV9#vPOVLjYK!nw1NVs}MYkP3sQ)$P)X
zNmbDPJiwbRhO>NsS0VW!%o4fJmpc_LHL4O7thiJivJmre6Ze|EU$Y2+Op{W57pWY*
z7|oZTV8vQ{v=$Kkj2MF$YoLPsd)-ktr85tjdPTa+f{tzT<tO+tg2nmClYDgO+JVn~
zgPzarS+(b-L%K{gFjI?-w$4zZv%9*2^*crynD?RZLz3WyAu}j&PD>p)japIM{`zA0
zA@`A^Fh?emMzHPbA=}c^xRy!4TdA<SzRImGdY*(wAZ~!S7=yoJBpx727IZF&_{agL
z4$M`~6cv<9x4OR^@+r}IxhNbq#n!M(2NYVPBSaa-`<_TfXyS#Wy$WwCxoLUKqvqT;
z(vgQwG%4x#9OgRbOCgxM=fzU6pr!qcMd8+}y&wU3RjcMfAeli?E*&Zc9;@RIc~I-i
za~WDMjo%eXh1F9A9$Po~4|f9$oRaBtr$Yj3I$zKgnR;3Oowv_mMCf8yCAqBny!>54
zZ|C)3D24Xvsu*x=w?+TuMv7uZ6wnO(Ze^{OPh=B}zTIy)NB}NMlEnDjUoUcf*?oWZ
z84VV7bKY_v|GjqMgXrZdk#k3X2qv4G;ubQd)CiEIf{4kwVXG@IF*fMETO(Xb8ar&{
z>5CG+#Ilmg=N)+S4S3;C#-8o#M8hy^RFYvkjbvG}B{2kvD$xUc?eJg1y>yXwn|i+x
z8RF#3?hFy&3I#14(ZBo$da84FvXGlG4+ENV<5GogXV<UH%8gva7No=6lkHMY!WK)?
zI{@9*dWCx6Fo115*z;pZlx0ZdT<`O*qUtu&Q$z1d>FueC3NUpU*~d?sK+=U&BY85%
zWqengIr-?bvLe+W7mA!8bRd!TUS`YZwzi-!OpPzpxJO|Wt$M*_IS^+}E{ZDP(qHXY
z;0zW)8vOYAZ!6aEOjrRGIj$w|ARXzDm$*a6pK*1a`&}~(8|Y$Op#z*oHrTnAp9a&4
zX{tWY_J`nObzdB<Oo;6J+?~)0Y-af*Cb8Pmwtr3;s0(}}UB)9HavE?!vNV`!JF={T
zSw22cXQYO`&oXU1Y{#y*#UNT&=3jcE9}*=%!}#iV%9~h|ND-|KC(aL@jfvDQb0ehK
zJizM+Y1EOM!ux`iuJ<V;78=-}k=(WV<LYK*2i}-~ewo@rkt$eI;XPwi*CU<uG*#8P
z)hkt*2S|9*j(&&)C5Z1-F{N#t_*7pg4^*<s=rKRm6Pn`phxk)<14FJyU1XYWMkG!~
zA1?vGnRv~%|2g@<9)N{S%e7Oqs?CR5Wc-Wu6JX3-3Zut-L~W|{<oQP4(ls0@zJ>L)
z-WTFzr=4YC!24@N!MVz`1~<`T`=hxx{ND%wGsns0{fJaDx*@H-Xy5)-<?z$wzK^mq
zX>*)T;xsWGFH$XepXRdQ<}fI*QS|t6!$ETcj7H>x_th4Ivr6j;i0NSwXWq`>!nTmq
z!@i9w6d)Ap_uXSuV6r5xHx{E(kzJ?;h+m7|9_ioOLeQ+N{vLe`{j2cOY{DMGo78fI
zV5#U`?Teil50hI4MhH?J`}6d_8F_xQ`85rh>dIR3nT947Q+_Z)JIsv`e0N{YM&4y4
z+A{}toM5#AfYY6-wif2_7mk%*h|CG0RMMRf+rsc5##ld5*!JGaPh17+T@7e15&@S2
z$+n+9apWq8uTyAZkp~);vK`940myE$s8@Z88K=YX&B@SC$ujG+(7tit)Ru&yf6^<n
zd0-Z)sQ>Nx<8?`7(i;UZ8zksd#7qg~0yx6_f*XOtNXz($5v4A3o>(E3S5ZVo<Z?B(
zzH*gFSvcGAjp)T+)O5;YAfxHJpKcPQ*&w+|D*D5q%K;M&%0hcQmjKJuurR{w1eieq
z0-eL>v4PL6^X=aBkHRzu1QVC}@bmdaVvg!6qqfJlshG>ck)Nx#Zx%cbzB}@+4<zRn
zsJk{EvpFCWU~YZoLxp4Cu7|y@&%t#xD0Hy7iS|kEs}3<3JxQ+Wm-JM*{!h;PwM4^`
z$L;n5rFD2?#oLyfh57kgRJgoxH0#EPeC3~!q*GryZDZvsfuz%A-dWpX_Lt4)pU48#
zTKHj|JLP&JsG${2|3+H?$kmzlxxaNrB@SNnxz}&LyRu40Jp!_tzV;ymBtC7#lU-CK
z8;?yown7HYuebP9nf*YxxBrEJBZlL<RJ}>r4a&gmNlKhix0AYdTfOK@e>CXzfTe<1
z8qupEmkedHvhLrE)!Owz3%gaNNr@EdT{op&9DDPH1hnqQ+J!Wke%_9`^;3u_Z#NoS
z)<tF5#qnDI8jO1|>hAWsABmMq!vFr5tO4U|uJs`!22c-x?=ZzDsGYPvH&s+l!{U~r
zfwLd<`j#!W$JLc5RPpBiR4<@83UHW|V9|$}3<l8o<)~3}fYRiaB1QfsDI{@-Hz{*c
z_#byL_QY!&=-3P)fmiK+G4q0F;fOwviSQko&kx%mZF+LKIzauYA&4xUEoqgX0H{}f
z`29AzW3gS!t=6v@OO|v7q<C12sa<P3zj`{RtWCs@%pM{Cg$pk?OyaLi*a(h7M&tBY
zkV59?&>b0Z-d9N~oLB9Qu*ODVO%8Hnw|)4=Dxe%rUV?vp%|xCs(0}LRQ<6{H6*<iE
zZzxuUQdy)b$-r3a@7J-N$$7ucdkgf`{Rt9^)e8~8T<Ft<ToT&+G5Ud|Fb<xGmZ2%8
zspS2_hSVRT(~^+#imGH5<6EAE+#5fyvf{Q7j9iemq1VrHie`aU;hi=N+BjrdaBy%K
z2TuYj->Yy+z66nfe<VpBuoUGX`Yi?O;Lrv1lb1IERuJyPy8t-xiD1&sBSxxJOEumC
zb%3*u{p7B$u0Fj4fk2}bhGOvb6stS{N!yZ<fx)cKcY&-oEK!6dIq6hM6dw1?#%WWj
z@N@hS1lab3Qy597y+D8WqPJ;Y{j_bv-fHvp{@9y0u7laZG$llBnQrSKHy|~b0!N6;
ze!26nHL%uT%_;(9Oi%;}kSk<;vPC1ROHL%f#Cn=bmISfKL$cfLtx%I*j;dulBL`lV
zU^{aq8D}I*c3X?rHk~NZ6m<8E;RM*bzC7y5YwlDa9%L02+JIdwLgOIh^t|}|#zkVp
zh>(w?-uH4WOWfaSk%fw9J=rw8m^ii*o?(?l)HXwD(i1VU^L{rn^e^n|U$~{Z;s8({
z{yN>C*OlQo<dA+(K8=iDHT(ymr3lSvKVY=AyJRNmdDSyoEMILlloJMpeE!T4{8eS)
zqX5sl%Z(Dfth`+Szunow(8AEMPqK@$U>fM>AJ2|lk&4$UkPoz=!5h4G3wl$#&siIZ
zu&<DHQn7D1QsEw#ATKs5HShFY;aLXl{Q+V`BeYc^%nbza?4Jhy?X5-VJ*p5Dg+t-B
zZ=U7Qj+fYhXzZE_3*DWTCarpwK!vouP73uWz-@P5Pc}mU42lJpg6HWV5>;aWX%zTD
zNUrs`2M4R`@hd+#86z$ujrZE;I9!TL?{aiDvt<7LsU@R;M`-@h5!<J4&qn3h`v7Xn
z6w9DIIYN?=o}A2t)G8*w>tdK8r``9TNH2$yk&lAr3nyFY4p;Abbkm3%hfhqPkl%cN
z=W(0r$X>rmM{RSsq0Nx=X&#{Wg5v4X2|!8k0Z(A{88y>U>?Dv<VXN&AS>mO?>NkTX
z_jtGbk9$}Hh3kXqEE^df<<@{eUvG=%I>tmCVNQB8OMZ7&;nSz@GXDzrC)o2u`8_Tr
zFHRmZV5^N{8-W31a3J)eH;0ncB1SamSUq1Kp=tz45C}MY#p(K}&O*jv+WQO+jLhRX
za854f-N-DCOs}7T0fM4&9<UM;)3F(t>ED(@iB!YOFTx+UZ*~DRyMo2fj;;CjpRUDp
zb-sIJ{n?~06eu<)oVlN(IwDsZ*4h1VE5v>TI0J<o=^q!+-*p3-FB_|O{nJ1ar$ZNs
z_U5XCLOv4kEvCxTnUHIVsWgL2xf44KY?3T_(i87dIfgBBh=oP*E<mvApBgGw0-DFx
zk0aBrm#4pMW)UE>wCvbXOhdqR_*{UTwd}*JgH&_N44OVnZCvz--MxquJ`Q0fRe%7i
zH#^bv9r?8Pe_q-l5ey=p-O7rEw(=DfFDfU8&h2`y7TV^A)`px5gw7a@1W?F#V}J-a
ztc3HGBo-s9dL-m*jTJ`jR`;|0_e`4}<h|nLPKr--nmUo<>@GAl%sGuII&5YKqpKmW
z@R~dz3*@`M8^|oft)Rwk(STkd={ZonSv*HF9ZRAAq~!5;MH!!=9w)!~<XgONo%UcP
zELy#<67>kvfO#8_LmrMdhvl1ZWb6s?EZW<`IP_n0jf}V^faCr5VG_K?JHRngWddRX
z$|kijkPNo4u;6lBR}g7-+ETN*sQT1~Op}1U6AciStvdj@G$AHYJKK}rS%7jiO3-;5
zS->Lm%eFv>2hw{u=n)7~B>+n7v70mC@LJwo1IIg0H5XD1l#>`{<PgtFQXFU`dm&^0
z@yW?6c6MbTWJ{rAfW`8YDh(h$33k2j^9TXQ=+)NN*7gz1BU+j197wl_eH-%P5a8HD
zu->lqh5h#2*GM_N867rTBzpwdTOaI6iHVCqSO^prVfE~s_l@TZ9y9&}2N6R1MOaVH
zO_z9yu}zURG>_G!LOe#?)7=e#8n|qY<OaiN#SH$<IVztK1~lr{+CsT9MoKhUY-H)I
zW@?JHW~>DS1Smw?SF(@yhqL_$GQ|v$WqngQy;cq{FZ-%IgiVD<Nnnlmcd!D=xmzcP
z%40^__aqryzTpu#2my+F1GlWkT)mgds)I0UA+36+Ejn7S(`X)mlOBk}e*uBE^X`Nd
ztIsbg&Mr=wk<L{xcR&az7EF&<`>KJG*wQV8tNn38oUg8RRbWDGoK>9q9GQyn9|l%H
zLST!v$M29u2X-3vBp4C-E(T$Zh+}ex4N*w@Mn7>ME8^L*$X%A?%xdpgebVI{B-G@j
zVSA%C{UVnze+eQjYeMtttI4h-^&uY#0|}pxy6TDbeuvlX;@@u+?H!8&fd*n3ZJQ84
zCi^L|v0&Xu)HKe>xwfRk$j(T9`mxq(N{P(W<c&Ek`lRMx@}a`f1><1q)8<D1=(?z1
ztJy;Pb&_PuLiaZ(54vX_q`Ko=*m9ilHJ|xvE~82U$A{LUc0kw4UA#MA^r?UnVWF=j
z2XpyW6M)hw^dbqxB{Ky?_2j3eK}0$Qmo091LnqWUtJ0=as0f}2)kziJ6BrWCGkebB
z##+zf;BlouP8$zD-UqZfEvUa2!lGUB@*R9vyVk7BqoZgK$q+DG0p+JW7gNI)sW{%L
zab~|APL;=}j;9EB$U4rU%dd?H63Ts|@u`gEZD7CI0gK__nt?|7oPRVL#SOL4mvO78
zF%KEZOhk;8*BS-B!BW;}p6^?Jzy>WL*+i1L9yc}wdf0>T;E6x9L8CFwLuLM)qeLQ&
zgSn&Ov+{D#dG2yy^{0S9uefK-4iso_SNu#)6=ptQhjxhFUn(PI*f+NrcvOr4y-i%V
z01FsNeI0x@M|^^N@>@M4TdMLY7z-WxmAylS;>)6!%k89)^LAc3-B(zU$na)iuCIs6
zf_2l`r29NTvCE*0xGsaC7zkPfF~l6s03}}-Vw4dB%6Uyv6)N(dXP<GSg11m|ge5_j
zfV@+KvvP}x52ZVpg*%#giFq?kxzVh>2<rie6TpHygN?@@BJIQDctVmzfbdpOX3~>j
z_ki{b+Xgt9HJK?XMm3mu?o{zE*7!-7N${Jl7^yUBcya_1%5XX2o-GGm<Ys@R>s-|M
zyYTYyT3}eJYnIE{3X69f9tHgX<`R|Bd>~T9_)S$;sOr(mc9&vR9-V0Dtuq!8lpdXQ
zc-ss>#}(fqP~xu3#<?Xqjef)T90H{2M&>7sPxFK*9R}wC3Q!{+k*t;@uA`i+B!l-!
zn^DNA`f$*C92KU*P4QdE`)oqHEk5TR22|MIH^!H$tH5fi+_F0mRBUVw_?8^7-yzx*
zML5Z^Z|z8%>`(Pn;VPj<|2jm&9&dbH^Z>M?lJ>VlB=JnRADNAr37E+YA5q3jvcFiz
zKK-O-kGdtEW`a1JwQJdWvOJP2QwqCau-#&kvX?wUf%tx?F@M33;hJ;0JFOm1Fo<?<
zWlf{PZ2mc$KN8UOY&J3QX{cnwXV7t>2sq!Or+SOH`^8qi*=n>y<Z%SvBVsh-dvK(%
z8i}X-+T~y!&6?jayQcBNQ)m)j@GGTF*^hXANa`L&1|EiGJyT8!P&26VX?=uYE{sRP
zBP%1jO#Ph`X3l{IC<O$>H>VI)3$CLmgA*jgScgyvpb5vL28zX_(2LhDxR?HudXd`}
zwuKk(TV)|`EQy(&jhaWfbq)x6C7~2xx}@T0tvqatP5~b8O`ANr9)%v3m%aQwe@=(5
zy^i}BtU7iQbZw)*vDNp+?NgvT%EzH8q>|gX1BCDAO|G6~Usm=v2W3Hh*QLN2opw*=
z(I_r_xR=<7q_8@KAQR@Go^hl<Y;g?eQYBj??<jkUm1Q#}@dTuVD*sKJ(NgD2tC~IO
z<S0SbGr1|i3ET+r%aFL)D$Gm(gmGmS4Q53wTlY2#S1xhTNh(q^iN|7A=N-&_WHnC1
zNLLJ#<IDV&q|52E3Ix4Y;1ejpT0r>1`*jDS1ZSYe-`XU4_w<wXq|pq|uxq;$`l}4r
zZtJk1*ebUrq1Kwvg8;b;WL@?pt5P|+hp+SLb1;#0oL5{Vk7u|ds=d;@6uj#xTQ0<U
zKsQ%Uf=N6`O=4yaaGXDQkEr_1rF|qnLW&*MPl)G`Qa&#|=ZGt?WIto_$wDy$lHM3M
zL>_<c2ATD$%24OO3y9V;I_`d{I6(Y|eCh2`>y5u5geQr;!_<R5Ck6v4<G*R+DhZa0
zrSIz^Gc0qAxVt-VDU~-(J#BIW3@7$eN}~y9K~3`{F08g*ns;yBEdnR3bS+!eBdff<
zEbNksf5s@Y?<|RC`QmY`9t2$#uZfJMBZL%KSun)BcbG`ZS3&t%Q$EgFuF9}$UQaOM
zEX{GSs?V7T2MePKj?{tw(S39rH1%?KV)_y(w9l`}UtE_g^fmNp6C85G;U`5UcD0#X
zqW8CZawwg%gbea6siE`B`MitO+!#v!d1~W8FZEh>xYA2f#?Yidu972QODx^Gf^vG#
zr8asAWT;-~+Wge~9B8Zo$hJWQXaFDisdSg0h$ptgNS@SgRO^n^p`J<~EHwR`8s}j4
z8-d;59{31GqB;iHHD5wb2y-cu!iLE^q#mi)vs5&4)geOSmpX#`^Ain9J_)cQWb{UJ
zZLNi2-6NZwd(2?xBuv6uK!yJG&w=~h&U^<&C_c4$((TnC!MelWuK^=d9e6?tLbBxX
zZ(w1v`e-II99ttuw_OiJM8VeN*J0d^_$t^0l{(Mna=v-HMNXxN-i=oTZjyN-BzNIG
zftgDv8H}e%7N&6AHAq=O#q*dQzDl?^(v};FOG$b3w&E{RPscTO$FZp6oI>4vVfqs0
z_5^TV+y_GND4IG!B2}Ihr<EtaDUSSScgYM~#a9T4C<H$o$~pmVV6jA-sWBMdhLhy}
zthQ}R-zA-yrVh109hVHA#IVdf&jDy8vMwM&=SCcuHPk~V=1prM?#_#J$Up1_ixixl
z5jk{cYH;?_PKkCgP9`c%X64eSW6Dn|IJew__LDkhTa_HRV(w|Ux%EbSc*hi2=#K6g
z9{U2+ORbF<uSgw<m_Jh>*0EeVj52~4nAXLNoBjK82!u|J#130g1z(UyTE;~VRJESQ
zLZEcze1KvTr^$HCdWA55;fPmEspI1BUaehWz-wu4?$7)tS&(OZzRBCx*0$(+xVWv2
zLp}J*)XJ!rxw*a^9XEYk|8)fB7h{-*OrJBM$<cCGHn4D<HC?2-0fJ7O&kzU%79d-F
zd7)hu<P8`~!(k*We%#?4tA%>!=})PNch<qpug`Cz7CX)A{(6i#`X5-r8ot<gr%_PM
zG<g)w6(&`)eyCyOfg<9Dou~B45WT%m1@d{vfl6W+P->{%w~GrUlrC|<8#D>>&lZKe
zl1~Vr@wqt~1}r6ldVse1<#q?XZ}xQC1Vl7EK&!P+Ina~jT#eIvjSa|yG->g>?=MMT
zj}<6ldjaxlHjvo4;$mZoECI)?2<XQFv;cS6eonA6CcAbj;&hKiUnU~quv(Wx5fVwf
zHTvq$Yk!)1!5I(o1@zuOZ*#yj$gYm6n{tuD9u61n`0paTS-W&H0@LotB^kv=h&AZl
z<=PNt642Bj=dv}*^e5QB>y<X7w+M?})|))p$o^-lU9(P8+VEK4OWnp#l_3Nyh>vCn
z^FX)j;t+!UYfOuZ_3`Z3JQve-YEHR&1!pd5YRAjEd)ciX(T#tUOt<s3Bc&DnUZN+8
zl&|-31ZH;6i1nnZOD+IkFzd^~s<Z;Z;j!P2u5jb9W3M?zE^YS}$LIx8?2}&tAW>ok
zQA8z-!D-nY0{V5f3g4e^uxd1>kkpZFmZvWB{DwY|^IpaT(1Gh0FtMY!2yRMB%STnq
zWoX=_c67FN!Cjwer7Q_g`Bx*d&9e0lo`^g<-A@cR`m7{n7wE4V!395PViXdK(spHa
zO)yX|m&s*Ss8>JZ{uwMy2Qh5Fj*pPl;Qyf7Rs_LP(a<2i0wgX;RCJuD0&A?}K&>?V
z2!raGJ7CcGbHi|n_Lp>1o$3DF^5ylkZTt>V<es0g>YZJaAq+nQ+OROg$vNqq0GhCE
zPI*~tHk6SKAUXFcMM#@YklAu)sNl9_hGY2p-y(v(YG}U%VBLBb0W}AOvVOf2W;&p%
zGA&$Nug!K>0tL`8P(c#T09A^lVhRstetv%7-BF~ExhS_924!1Rgo9IyoR!B>p%Uqq
z%W3@yTlc~%fBIEKQV1N28Urt34%2_NJVYscuFY!0%1O9BlOLMxXA*1rZu+yF_7>G;
zRs<^4lkw$M60V5jE+gm#oU6|ISe6|=YVq=j!@}lk49$%fJ{7+T8Qf%fIoT|O319Rg
z%U#+YOXhO|0m*!W?CRI|6Mq$6%ykr|uJ0I(@+A_~92OaiQ8xd14y#=H2}2u5a{AW9
z9m{t8Z;z#1l@X~-o{h_VRlFe4)_dV~bs9~i#Q%cd(zRD;fX~F~afR#c_4(8#DIlHZ
zMC9c2Xf=1?YLVPuO|`57j$Awe7}9G|m|n1B6E#FWZR9i{>#flC*=cv~r!}MFZV|z#
zbGnw;q;%|D3pmduS%6U24qXLIv9UG~85b6T#t;2i$>NFFhnL%oqh}T$JD_E0TYk*H
zxuyqeWcXv?PfmCP%DJ0OQWM}P*3a;=k%se!s8kl>QqqAPt`knW*SB8k*H^sh>WEEF
z!54Jb7to-`-LhL(wf{;+%WN}g3`bg%-*Foms+!RYG#ST&ih64AZ#KjPnE=viH(@BL
z0m}DB8zV(Xwx|@n4k9yJ)BX9i?+9s_MzGxdt8FQcMTuBi|04^KP?U8#g%r9m(=r%E
zG>aDtv-BY&$+RSa^~#jJ7zt%l(DgMedXAt8^%dPmJZWdqAL@w?xeUb}K&B}V>q3h4
zCrm1=??d|M`n!9o+o87ojy`K)Fb6l@!U(7Of*%(nzo1=cYB2^`J{{HVq@(xIv!-eX
zl{s_QQ?sjgk6yR8N67S7Z4u?2{JialT(zGYlJ0^i{S4gW%qT(z-fT{OPi3aMy*e8k
z5m>j$6D$*#NULP)uCrh9%PqzXW6Z5{eI<$61)+iZJHN+aS>z=Fe9M~2hE=}x>oh>Z
zaR3;2X)OdSz=<Zg^~$RuB5tGuqWMR@?P;*W=VQf5{)1N?nr@%Nf&x4wcuxl^J%QG$
zfAPh!P>I70cc)!e{d6aXRW#pZ%|I+o!b<ZIx;NAyz*Q}Winl?)$Swf<L5^kP?rM<3
zjXa;c{g)~#QR`x=o5MES)EC!iBpXMDk+JSRlnk$L?&N!Rsz%V{p~bU!U#k1(t3FH5
zN_5}u86{W!IS3xw070z(7JVJzkHd-E1iD-M0b!|h9n*1r0CVu2;(O?Ql1YRrUu&5_
zd*`J_z)!DDX>UNp(a3HMKpPX(65(rqfB9?c?IV*K7@CaE&IVwNOk9zdJ_ciA<}>do
z6yYhi(+MKzKWg&y2|0XBR7%04x6l?eZU{3WQLH!ENWm56PI^yz_R<c+_GHqd@MYSP
zzMdXcihyH*5=TcDU2H#~jS<L(Z~wi9;|I*7kFnMC$6_?Jv`p<8o%88B?^03@0p64(
zaL`l}`*m&M&`rQle!8}?fiXJ10;FENr7u4XTfDd1c_%Iajb<Eb_vuqX{hsqXt>>Hw
zleO6bGo(I>NcckLHp&F@?UW@|Xgl27ImT)e#6o|9>tNIQLprVD1+-m#gd4H1y`S-p
zWJrUqNt=Iq7%MKpp(tjlLQZ1sae33f5Bfq`(EfyMk9~O0B=#Svi?A{E`Rnd(e~BZY
zpa=p~q`Y|kjJWqFuSnWF<^UT`@}TiVX%pxWn7q|M88T_PIhKDgGo7(Ayss?Ij!iUv
z2O$c>=lR+jfn%4AH0b&ngq1;klH290=`a}ebLSu@xsr(<6JtP{vZU}6ac^=zb5ffI
z_JCRz7wp{D_b4L{XuxF$AS5qE^{34&Z&U%ELWtP;oZ|$8*a^OxF%7BJ6_rEpi<rd3
zLZwH&7(uG37q{n&&Z6je7(w{7Y#Zq=2G2POL8<=HMOg}!bq3Q^;TBxhy-)Qd^79-X
z)oJA&4Hqa<I*VH&0*wPKCd-l=0j;M*&l4!{w?!C9HeS?NPP{|*V5DE%0o?uN?URf4
zc8RmBE_TOZ1vrN#m~~QS<M|p<#^Gl{qnDxuMlYG0g8Ij^>HGx)MYP9@$B<-==weYx
zMD%JCoX#-`6zy&RN<sTy*W$bkGJYFNuegeA_Vu}0p{@VO9qXm8*vAhjORKG@_!`RZ
z%8vEb)Cgr`h#LR>m5%(Wt|YjX2ejLaEA2O5+n!y7n2<s!!#leb@?ZfNLyUk<^Mh6Q
zmx8_uVB}=@c5=XuRaRH}(1;`k2!T$F^|)g?DYH(X`={`m_a0L19O2;Ub^(O4X@E@o
zfkXSJ41K<T!`=rx=_><jL8qrLgH%0`MrVY%N@Q&C*4sCPu{D(Mqx(241um-09#w(3
zKbf=V1$U2zGGr97_>Zo%Et_Z*{CQt%w<yZr&w+za!i&2J;HQC22B04KIIpCXsX(N7
z)m*3gVi~LsM9aNur$14OC|$PAJoDK5soh?NDPD-RgSon~Q9<@8V(h)ico;h68lA2C
zcRe0UZlDQMlWevwuEMaTc^7b)fq3UZwl+UiAxEm>mw;2sf~?n4_*(%g=`{WkfU{v5
zIeJ&aFVj$_gY!j>5<j=6?2U(hHRdA{SLX*6i`v!g5(aU&Ww&opDJ##t_kjB3C2BVe
zlzUQ}lp8Gz&Ps9(2W)I@TeI)9Vuh=KdK3jXT_wesSj1pWZ9V(Ifpj75H6ZTV6v7(A
z;Xt$1J<x<l2sCuU0<xTUCXsDF>16<2b%6s-pQjfg1mGG~4xpuFYoau9&T&Y@0q}m_
z0fmvKEjgs?P;n%wU?j9prFs--2e2_6yKJ>=NA@Xkp(1tlOTcS(_LYD<Lb}m!Y88kX
z@qv~Q6CI%UUhrof5V>dkh$p#}08JSgVMg@Jvp0<+q`@zY@YX7gJ8G=2fQAgeTu6>F
zy&6Q~Yba_{d0l*7?Gd2fj7Wrp#V!ac6n$>%Lv-B(<>@OC5s|u44j96SL|-BBkmJqg
zzpa1G9io$yix*-!nv51O_botuM*E}%k_!I{2)La#Rp3BB1QB9AwesU}fW;=jRhfXv
z<)(a9g$!nh=<2)VzEx#g0914>HVVu~mlSBAAI-po4BY?if@EP*bZhi(CLh#mZS}t!
zA&t94%m}IuWqw|t_`af|&9!#XXSF`N;1%*6wu_yjI^a_UXW&rjZf_-}2n2s*3mBS)
zt=adK>^xE$LfR_jXJW9vLT3Ov8hFNFzY5j(0ysv)#-6_rrh4*?iHVmrCd*_rs>m*o
z12l~^nBJWG&i9#AGB=T>mxHd_v={-Y*Q%JP_8AEY1w$L|3%-tSG*gZ~3m5`i`(#X|
z+k&QgJDG+fB26%kXXOaf-4mq%F&dC@r<0%I00L;eDm)na5yDM7)r-k+)c%vCueDG!
z54LgSttUev!*XKZCBhPGZ*#d(HN-B32*KF*hn@eWsp`F06N$d`+=H&ZUU;RpenQzo
zC1Vgj;NvsWD=TAaz1HGm+Q>KM2I}eE*d<yBGrdccYZZ<>VB#wGZD9%HUJmpq2R>ZU
z(RY?6d3ABKQvps3h+H9=C(?lq%|^jIcRJKG`EMJzX_tVez>y5q$)K!Y+8^@nH(m!~
zQzxwD-x+URk+QnHcbGIp@bFt%j#Gs~w@|cqZwNRJ8zC*IC){`qAQBr;CIxq67@rR@
zh=ho>uN}xy5h}Nez!w%Xt-PqN>jICVUn5(357InGD~%<aB0L;$DXV)u9H^%>PSdqt
znPeI=(voKQ!p<2pgQJf68#L&_jn<+IjWGU|6xrm+9&;#3KHt(})WF7uIk2+*LhhiA
z>*Kcm8_xUwlgsZ4-CiMMy&Ro%Cr6td#7tmAuEpbFNqo(<$F)E6UI6{(msio(j<4<4
zplR}V5}(GYnDUJ(c^{J~6kI>kaV;@uqWV1t0wuGlf8q~!gY#G|DC}MBz+na|&+g2*
zxkjOvVeubipPRf-G2{}Wr41zPI5c(e!Aqv*hqO_ITVEcpUG-zELy47$U3;19%Nt?m
zOEfohxny6x{(?6eWkD^bzOtZmdT&<-Q6te03-ONpi~Sd0!O%NFZ#Y}^@iKWX4x^a?
z<VF8kG?$^dD`-fk#ylF~iPp~PzG=?rQLXn!S@1~8Vx&5r)&9I>Dyw<5!3k&?bZhY>
ziwsZYjgMyw-{Etx-!RpPY`MECML0WNJJ0!CqFru}#@1ErjOLWH)X2pSQ;;c32T~+(
z>F6_*>INt0?brJqI+vymtYP!U3t$~%Pa5>tyChv(&NX<FCcrt4SY7TuBsVA>z~8ap
zM?@RS%ggf&Qu5KK^^WSht0bF!2kWkxZuzpZu;@bn)DZRnADCfGJ(O#Vy#pNeiOQ%?
z0Yv<XewjDz*Y<2n&u=eZ-9}3rY^>?Y+(|(ejt^9#VLTf;y(}?DdZWG@(ufO)*TUOw
zzIEuT*fGJB>hyE){WCF9JHmxyu76&xm#OH4ZnVim2Y2<yT(k!Jv`j1M7oVD74jn$F
zog&qr-H2?TOCx&u^m<O{pf}YA6)QA%LXhqgH!W$da>;)Hdnn!os8URPdD_YxVWRT(
zr2Ro+dj2APZ1$NagYMpFTYmzp0iEKU%UVBZl;kOcTQShP>rQ?2H1B(&y>ffc$&GUW
z2FOv;^N*a%a>|KoL}Hb)_tJs^WLiP5N;%{C;Ki(1q$871fg+3UTiLPQqTGm4tLRvU
z8|PJSh^Q+@=6*ovNiX|IuV?+qLVRCchA@J&nbTX2#mCMxP3CU(HpFVbL*Q*n^=CSZ
z(43_{r*pBharOM>e*aRv8D85NDN=Sl#_+jw)ejN^yA&I210T@g9Iw;FlvC==P>%Ln
z@JE9`OL=buVBXa3LOW3pK$q!6_dr&&Z>&B@<3Y>N9&s6T1VcNqj|?<HD$_0o(Xgu!
zlilk>2ZPnx0d7y%{(jFC2pH@YZO?j~Dy1ao0ovNF0Mp!Xw*ReIafNdE3?WP#!98@)
zfY}Y5`@tJR`sECl7YijZm#(WBou!G<L2>=UsXK`NUetg@UP`L{=Answ&trREkKi<S
zr&W^Pl+ib%!})8YufDxe-q?V<I&D02VZ$hyK;5xS!uXl;BA*xrI1`3mt&&<ws21Zj
z@YU~n<>Bz%-K%P+5n+o7TX`LRmtQ<K(~#o?_V}7poWA14x1<Bf*;8#=x;FW=sI*|(
zr@T!#^11flYh;fq%1TF`{Q#$XJby5aBbKPBD8)+B<?{|m5{(qIK-SxWqjSJfDuEfA
z5)TmjdGbedG3zrw!9ruE*bjtxnAj;S$s4!RIsN$bI|iuCv_xRfS0oEj{@R)91;efn
zkAB$u_)wSHj-LE}qbFbIYE~`@`E${Vi64*E6xi61NC2s(KKcsVQ6Xh~;>wqm)h;78
zHA4OzFO+CG+_jMF<REpO;^@1ciJ66WcFp(1*FI=1*!ua?txUnh@B6=L$<JO19BUi+
z);LyQDrr57KA~)QY+L$zwx8B#FpjibwT50wxFYV0&4+$i@(<Gnn_me)7xKH(gx_cI
z(lo*mAPO%X*LUCkE3C1dmI%`q-hgk^+D^=MO&PK^&v__kzOebw;L7H3r1$>iYh9k$
z<RAAjdui4wu{KK?ZAUrFbQTL`#RGR%<<<>nT1x+dtw+kgml9*~+@|nau=@N?-^i$q
zSGc{!w`h?TQXErDWT0rc;bn+YOy<=WtYOH|-fDPbt^)YOx>-ZlHi!cyIV@$TR|cCe
za@6QV<$Uy9xn6w9whFj-=YP}HNVYoxD!*HeRj%<}C6SD%GS>l`m3UgB&*co8qm|MH
zi$l>f-u9}mVq}sDec7#xfO$%x^#;K_=)kv_lopDC(lng|fg90wy@dgpjqeR0qOV|{
ze5>qKPoA4Nn>35iqo8s%GB1fg1zko6D|&;6xMUvbeo;7aMPGb;)O~wo6m%<;5y}C7
z(Okrsyz(}UbLbJss^&05W5H7y;NfZ@tVN^~z4U4kyy7|V=rTW%nuumO!Y-%<u=%FA
zK<~KhWg3aT=v6o<uM-~PP)rKPnyvW7@6bW>KyLY}V+rNQ2Cr}_`JCN;4r?Bl=8NYR
zRGTjtj=F#QZaQ)!Fj^$X&E!Dh(c8ta@jF#Se+558ax~p8^K`j6PYg<M*~RzXtwk2}
z?x}@#G|cPxxm%D+9!wfSb%K*2JwwQsrrf+lg%CK#erA?OX3^2aqz#X#4)Pd_5{KMG
zWvsr6ATV2fWI7k?`PP7Lp2`37O9G~oMH~xdI7C#u`-HKdKO(KgYg!?#idHAl6$GUj
z`YQw$p3C%<<M+CI`5U}LIJhxOSDu~Rz>tm|=Z}pYW{RziKIi~7Dy5p*bhNMUj`uyA
zzh-q<i%bkm-7>xjaM|XB)L+o|0ZqehBsVS*FuMSU4E{(~R66KtU>E|W*>N=WAk*o^
z9f#qHU^DJ$9M3ma)Th`}(YBqB4ix7&5PL0-%b9gx;b<tzB7wca2UXH5uCE?f2WeL+
z?rEpe-Oiq3c|w=rJS>Lj7sBK^=qlxhidq`z95DmV%{b2$`*x^+0WStag^mS{H-yfC
zPl~!or97kcT%_8W`B|!Tw;?3c{X?MJ*#JV0$#Nj6Gw2-0-be&xahvRS#b}HtAnRnY
zmju1jv!_0g(2>)NVfUg|`V(Dshj*P9Xh-=1oEiOT)hiBrG@1YTZMNJ8eWuTdC(aj-
zwP|_#9Ku4?qrS~ot+TNY**mjcx-`eWajd3u@f_Jc<U71DiR^>>T&LdISR1IlYjZt|
zPh8t|yraiB6V?B+c%UNZg2qC^)HHC4wNde*IDWl^q(4n<L^cfjWL_qQ{)~b*A=J=^
zm0iT4VHfzsq61LHZez6fbU~!Mdlc6?WW=+>CI>NqN1W`r$IB)KZ@<Oai{P3wQ)ETu
zKhGYP>Shll9LhH~G=MCLOUt(@EbT+*QnA7!6a13fzip`09r=2INQ8aBCsX!pB##1l
zhv;{E>12rOCiyO!V3kL#?HvtQ?KxbaDb+`=paa<(f{HsqEUkr9*IbniD(eEBCXdK<
z#kHLD+C(*%GGF<zwS9dG=%-$0?N(E`U#NYk#5rF0oO5qV{o@d<vy`Fc1gCQvK0^=q
z?vI0o(axro<yZ3hdO<5XE7j3fQta7NaZXtMPaC@Pzg4{2Z_?#;G1hC8qNoRaJvM_q
zQ(V*H*z7V^z18K>J+SUxyT;<1i$pg4ed6#+nAtE(*^z=?{moeWygIb=M+5ED`Z)Ia
zzrcg7<<Y*dxodO1`gca~Z}O(tr1cjLh>-4lk89TRW=8q%dat;B+9cq685pbS=VBVt
zepmq>4)?LjqJ5gYHsc8%38g9*qMwc5NuL7!ViwX0lEH{DV=GctaHg8Ls=cZ}l$M69
zXoUF_2tw8|9L#VMuFDRNjK-g)gYwk(e#^tKm-5bj92pnQS7(<8Lxg)uN(LRqB}Uh}
zx*QYxjG5;jOLoIoEllT7fmKc><ZWhe_Bv(_*JgglzM|io8}JQ1%&zkpS#R&GX9dqQ
zFK~?0Uw9|=QR`)M+a|P=dXZu%+hK!>f^XL`%_<Z>;zh1%=b11zXp#I}qWeskb{gr)
zIj*zE{9tcVr4U5(quEm<8xq`(*H`YBj2HvOrmGvi(<0kB7uUJ$k2FO5z@N)<^OZ}^
zBe3ZAVv-EOH!P1vj{~hi8`-zsT^+eVx%R~yJwWF#DxQ%p2bsiKrLCM`YtWxuhB~AO
zE6f}=MJMt@1fwx-oO>C6T%xORo$8iJt@pn%T_r$pMSvsA*n4%;`hS#^$VWv-3amp(
zCSG;kp0GDQ$G<&qsaEfB=gQjo7qeHc=yLD_(ItZWBVW3_%*9w|Z#m9Bmvw*7RB_%h
zp>D_S{!<R|xE<s90?@19>9x^mC%q`Jv^?G0C=m+tgw%P0-nvU8WP5<*bIj%(#f};>
z8wO;df&L4FXo8+jap2kZ3XPBLHFQ9&ZajH#hDG9`=ZjHfi2kR1TW{U9w9uc4H7c8e
z-ck`_AeTOKtsmt+U0W0VkZ+NL>%PgcrI+h#<zc^z5<gu(ifJ5vK)CBGyBKZN<vKE~
z#|Ki)>(ouK4FdXQKmHfCAz};D!F+1#EvOm6Fo!s=LpN%W{aew$qN{wP$%e%K6=K&K
zcW@c)t<7z@<L!5e+K!`Rd3ii9U+t$|fh@`mT-;{kb$hqrhxPR7jbc{ijmOstprhR%
z4p&Tto7~~Q<q?eJjq7Jqx4Qd{*ECxmlW(^iyYpvs$$0IS?xVb)S?RR}tBH$iPW6SP
z@20sQmzK)f(YVH>xpjk%J7bl%Wn^Ap>h&yYJibfZu}XzW#I^AKgc-I!Izq@+93K!e
zt^CRAXNxsf>f4;p{(jRiTOPADYV)&!p+MfMZs9fvH&c&)-sg@S^;&wr(PYt8J4`S8
zW;x_~^^dv|5tBjS2-l-AE4}s4J`FtUNgl}Z!oNXzg6o_Qp%@wqZ~y##;C9%i?nQEo
zS#hj=OIUfSMgmQ9MkB7ly0_j-1fVA|<x5o9$iO$mTmpyeftGJ^hY)o^+lDhzCM*gw
zpkb{d4Y1@LCP)%@M2E41Wl~xIOSz2z9G#8f)RqHvSff^!pT(Pnqf@b2bzEZoXsu^E
zxNzVk{*bEuquOCLvN(2i5#Hpy)C@Fj&Oo87rd!)g@_#n?JLy(jB-e1Ju68VHARg2j
zTB4WRHT9-9gc0V@ae2wPbB|l+ss4XTZ5Ib^*A`~jB2CSD3XUyK-2sLodXV}*&*grG
z@LTL+sLj>nd!BS{%shPqOZhq&GWQ5|RbXnNKKD9f)T8vZX|j668`_4nciv}b^+RI@
zlN@0Ej7yvPx!YS)w=b)*0$o~F6$EOQPge5D45J>A-Rmm}9#~EvM433B(ot;wygut;
zUmYwRO7JePdb1oYGpP!6#o#w3@+~;UTH!1*d)1nQ$qs(yl5!FAQu>`>>CL3y{JVeI
z<mTvhc@&(tk&V%xl2f^*Qtij|yvbH*&!dRP`p^4_ZLgXd>xmEEnS#=5!PMS)PV;VL
zZbyM;Kl#Fhy@%UGfTsP1zF1}90!Q5frDQs>yTkN<8OF?O9=Wj2{MiMMzSCq!=&)jB
zQ7GeyQ;zRQxLAQg+zV9Y7<DB9=?uWU*^^evX3n1!hXZZa_z2A@J@7SOIlJ~jQ)z_J
z@WsrtxNoOVGtc%JVh#|&_(12aI;f)vlf0|$a9MtpTAOyh@qngUw|`wtg;0Nbe3)q>
z@=jP2qL9I?EPMh>bM6Kt@IPq*4JK*<$v-|7t5tp6YOl^Hec#5LX^`t$!B>LezH9zu
zte>-oM2eBLD(~sW=xkeBXnd*Gh!+LvOK!FZj*%2GA79|Z7@CSw==VDm@Uw`9)n7vU
z2_}&{Ea`w;b?4SKQSZ}l<06~65|IeFfA7kh*Y8&|vd<CO!PNT{(U+DT@@Rk-%%UBr
zD4*m=FeXDRzH0te)lr3w&7e^V>*NG#ARZl8b_6D&L?;PQ=j3eTl+yb0i-r%D@>UTn
zuhs9DGf|ya`F_-d(a;>9XK2vLO+c~QvE5(<_FMTK6S>WYTCIlTD_qJ3tT)XDyoS*q
z*!6!)_&;qhv>d|2-7c<vW)-$MTv8fUC5EeAA*U4O!ukDc8r5>RT&3bTDh8KzX4O&G
zzhA~3hfmb7+#A*3lp7Ac5#|^QDJScbrIOviN$f5@1ij=Qmj_z)hTY38J_WHX=GevA
z)AvPEUt3O9>XHoTasWZ?W<7vWny96{ZC21vk<Lj>4eS0r$TIAf#G)NQ-9w(58K=s@
z=t{<}lk=;yOS<k4_7hIOj~~J0Z}5*=K3+Q9dU$&qYZR!`7)C#CDPe{9{69pUbySqw
z_y1K?ln_xmmF|X-G?1Z@7LbNPx`!47q=)X7?(U(K?(UTC1_7z>nfv*z_50_pyRPfH
zo_U^g_St9e_kO*dmzNhR)JL;NPJW`T{YmlbDFd^Tm+eadHRcN`q(14_oKb;Z<7S2%
zS`N{o&xAGq?pN2yG4Vv8jnuVeIW`<(tLI4fi;^7Pq;u#$&X;@<SXA6&J!$k6CS5R5
z<W22ccBu!R>+x!*_4JExB1Ws0y^qgm1%+i3AXd0!ynGNwUM~gBT@slr7B)N&F)1rZ
zqDnR|P9rJe-Pos3p`+z!9<xpYj8KE8A+((P<R)&bYa0a+ya}EspJswX#tWq3A<&v{
z$So2QCrZv2l+dioRzvEh`^7bKu?c=WzXV^NpxGGW7U$>}6l@m$hW$-P(Nw@g9ckBE
zi`SDVH4~XSLpj!0*LL921nd|gB#DXty}+7T2h#4oo)~CwVtdgm-47oIi@|e+em-?m
z{Fq23vKId-au0)3vjs`|NIa70JcL=bIBKx@LXqC0`Eag6k)2uixn<n$E*A0m08!of
zTgP6s^S3|QaDv-9LvJl);yWCTli8}!G)uCxa&~6k#Yu=IG9`&W>&pK!snt0n&xhWc
z%dafyU{Qn>%eeIs(JpoWlVYvmfbHet`ipTgMi|xE%BQzrOgx1}o1;@D`>0W%@!M4C
z7nnxH5K+q=Up9H%gEq7b%EFocWw-$G>uDPs0{(M`m<ObX_Ca@N%jt(mGKN{`a>l}8
z7bdAf_K~p0|Gr{yOs$ojk+wTWw;<AfiN*FFHRZ0Z5sZk}Ct)KdN+PW{s#nw9NSTVj
zy1CEGb%{R)DjTLqO{C>8rw|)wHIbhz_`(BbIwpiC!qOwUG|WS-Gzyu)J8Q?oHYdn6
zn@?|t@)o}c=<CDMIuoQ96rZU!qc6)fB2p@w(ouEDMwY_3Ur-Ww7Os#)nje8hTZPsm
zmZW2dw6$PP;^OeaH9%VPotHg(m_e6j`Kh-r$|!yd`G$=xD&tWO`(=T2<-jqfB?;I9
z+v#z<uGM&i_+5dppyhpwj6blTbC(R4pN_#P0)6A_9h=!P`hj*^xQq@yLl|>o&~NYu
zE6ceikx<*YvPRh98Kj4QBZ>a<a(GajiHRkgI%Jpj=3g=uVUmaYR8VaAVN_in%u*vM
z_5NB~bO0~T8dU+C6(UiiPk|%6<?#ZCDaQ6+#hSdtnRKVQF{8Q$b4ydRh$uylF<A?j
z>@lk$iO;#g=&H993&%vRK8S6fi?0vSW+n#%W@TCNZB<pXXQe$)?QNnCNXS>--YNRN
zz5WRpI_UfJ8I(S+776hQx{vXhvob4{Nwc8Ktu-I(j4VxGQjV0rZ}r{Iu2xrdvo!r&
z2PFspOi0woc&zu3dX%xQHqRo=GjV1Ulip70i=YJYBX2Lq?vbowg&&DCE!HL^=<C_l
ze@m$t-fZRt!9+YuBwA}vS7$caKWWanmX91CjNXOCd)zYWxQV>y8ciSUR981SZoX@z
z&scO<qWE^IpkAu`9<=?)1vL9)EtW+|aua7NdAiv-Ayv?2FG!_Iag!pXic@t6F=sLU
zGo@JH3cQn$s042XWi>JfC228|9QW_4{YKFy{TX}-#rI6YC;$^dG33O?)ek&i3l*1t
zPXx(%NJELAuSB6$BeGWQKo7fT^)+?tx3{Zz!55Ef7X-9SDMr4-Q>5l=`X7G2ff~pV
zn9pMdVZhShuZVd4k$DzF@8-t{V)Y-8e}k$*sxBXu3siQ@jZM4)JE{syMflE-@mYj?
zld<OE13zNVQ?f((rwF6IB(9>){Fu>K$zG$pmF#MpmB;1h+O9nd^T_Vkeq{R?2b=v#
zrT+T*Z!mYuG)kw9>CqP|)YQ6%-Wqh^4a|rFEmNzdsoeuel|nY=*YN9U_>Po|268oS
z52@ho^vOpLQf0!a&ZsLl{)StWi_>!eyfa=gsdxzhkqBMS=9B!%`6B>t-Sh~V2fGym
z@Qc0_$mCwzbh#9bX3}_X9(}TaCB5M8l-S=XyHDy`Ak4C{u!M`^2x_u5ohtGlTw`WF
zTBt$%5T742JsGjEAuw|bo!C-534469(a&tBRDc;J7SEO>c1&3Cv~<>SEkkb%Z*0#@
zN-FbLRaFdrNyxmcMQ#XQY;ick9;_Xu%%}ima2uqd&uQW@o1VbWJQ~l`z)rMPSdqV|
zJhn9D=H~fkAdBzv(OPLX8@64Wr&Vqrk8s}5a^4A6;5yDCq@m|#4Kt7H>q8#zK^D8d
zrn*z5x*rFyXd!vRhyoDEydtH~HBM<Vm9}N;!?tDc9WF=}F(*RTiD;Ase*Sx`2W&97
zPf9#(zt+tH3lvgSiGG}H`$3zHh2y_B2UoZTVgS6QYzLETOH<L}KUu#*&i@7f{=A%M
zDosQ5EshXCk0Nz4@y%EmTk62;carYL458s?3nsSJt~QUi^tk001)0G8k@@cCTP}4v
z1SR|88?lU^KVPAquP#rPVgj!8WfRY*iY}AXi{!gf{HPJLGbwK{G7m2GLCDh7WDukf
zJm6tuK<=<NQyqywu;eY41~6e9$tzi!=DoRocp_V-_cvPN`Q3u7h2LX-lDB2qPpFBM
zGvjOX=KHqPscYS&OTtnGhchK#?kxrQba9CcM=56LONiSkC6a~3fd5PBJh^iv+hJ|{
z1wd$wy@`*H*MMx)BFVEL4q$CZd{*2(F00EcVpN3w{`m)dgMg@*7zrCrOVbQ?C+fVX
zkg6ZazIvejpSh4EzIyoY44!V`R4*g4?iA0W|JE0qmU@^3(HqM?xp?`=hg0_63BHMw
z!6}^xjC;x2W~@P-d+Xa6uI=>^eGg+!%Iiz9;XxfnHnwQt^O-Lp{MDhSQyw0GZSY@A
z`0tS)gGatkN7eK*=zd>*d9tm(rD<@&Lr7Jv%GY#<LRw5q(;E9TB~=YAlVPekToZG5
z@Z9fS{~`$Opn3UcS%t$b)T3WH+W_`1Cw#<eTcaX-h$wtP*teUfjU2t_V#s<cYlH6X
zsWr#3B0DR10pDi@>1qk^GD8wIWHJF&?SKTQXl_wVk^b>oex_lt{nm&I$YnxN#U9W!
zYkj9r!xjIVjIPPwiSs0P=KhwvvKo<t5^3B?heoyDj`nI#fEXs>V{t5llU51AWHlc%
zDOgp)JMq*|;@@$Jsq6*PlU>VVF`;IGQC#8MQ)L+iG8G`)H_#~-qR9hPSB`AB<os3l
zFuC`)O<v6pE9ij~34AgxW5j>Pp5N}K=^^G^u#|Y)mhhs>SC&p*;+AaIP0H)b;zr35
zM43*f^9%kSrs-&00fB$@guOXp3Fq!}ipRc5UMzW|U$D93e%!ECH!Eb3<{X=pE{GuB
z<H+!PmFGR~JSR9JVauAcM^pec-!nf}Juraim9==5@?s}Dp42o0sz>uoES4l+o6ORb
zbSay!vDgqcONL0G0{w#0#2GjBa^2#Y-A**&bzX2l7<}Cl&<nHA9~JGf_mSFsW8L7(
z*$^DAdA|6^GxznVp<3tym&xW>DGSm4OJO(lmhZOzMJ}KBXp9qhuW`VcPzNIDM2Yjg
z@SuG$(S$2Q0yfRuj8_cP*CYX-B>$|d_g20M;#~~T8)Jzw0pm5JA*J!oEIb7Di5gIm
z+K1fCt+|YqLM<)7#?{M^d1qfi#Lh-{?O3M+k4p}+5L5u4rb}{O`ChQx=&-T{_*xa6
zt8!uQldqq&`9@*m1->4End%DgqR3N!qOrwGnj!@CG7+*I*rM~C)uve)M6%r8(f$TM
zD8hG>%mF>@8E;H>BB82<5nNfQt<q$a&pd;iHkbNrdu`L#S))M;sZeh`^|Q(SEqteA
z@#ok;Al{{~DYph&wI!?i{?-2XM2*<uvTW%*K}tv!1Ea5kk`ttgwVXT4*+F#|oY1kR
zH>zsJ3(vs1F@1dv&^6oH4wobCHoOvPbn{~_F4*VPFA?wT;XTkbBRK<f`-pDu&Y(&9
z7DU(r+jWCzvOwd!_VSYJwv?2$QZLQH#oXA)%VvEQ5OpnPGQ&^#t81_tc5V4r3U4Dm
zKfh_yCYagzW=~0MJ=|^!6#bI3d=?J^!n+E4dMABNOrF|}dU<ocV3St-Lz=$^?oL{R
zvKIDRA5Ma~8xu9a@T%zxnM_c_>h)^TS65V{+2RC9HRmaz-uk>E!vC`QEM3scNH#Py
zlJbTIucUY@Ou}q2jx<KUOzub#&wPuJ$<xL2b|w-<%0w^i^#2=(=G1;aY|)^J)OscL
z{0YR;RMHn*r}$y7<-nl`Z^dVXNI7VVQV%j3P`Y2|3Egf!L1on410n7gQ@pR?dqF;!
z1i5}VB>yEshL)z0Xd@8;Wv90zzcA|ag4q;q;tRmJ%d8sb`YB`mmw+ddzqhJp`)j7z
zH*(gU+C1zASyaA=x-x+ijV_C=wTLZnQI=J-(}^NKd;wX8HfPrxswxxR{Sqm)0f#b>
z?a#?YqW50F{n$PVP_W)0L&IJ#6f?yUiLf=q%7agf>6L;sKEltlEDkON8O|$yh~Pfy
zyZ3S2UyBE_E}Ta`!X=4W)u&C47wQkXg4l)Ff$BZ_Qf`<d0u8Rz`@z-b#eS~;Eh!Hl
zcIJKI=DwFK*6?J4AWpg<N2S>rC_^yG54l2>?Q8<>=0CboU_beSoKCd}h^p7=V0c)d
z(KcC&v2=_ztElo^-%7>E&@kUfi#pn*WQhyDnfc^|u3BNlQFyX))Nm>cmx|g&JZYo)
zel8t3?9T<~2wXnrkQ44Is2PH#6ZibNB?E}tDFmZr7OA%TdvGr^clT?IuOF5cIBoSI
z#ViJ%EnWfhw-kI{;bb5m#%{mEzOWj}RhK{~F^V$t*}ocdVd|(nvNR=3jD3^wIL-Yl
zPw~{+O6}a(`hT~rFCxW^iaj6ml{)umxS7RCc(LiuCTzYS0zaUR(ZU__K-Ld+3}4WJ
zn)5hGm$@Pu;}Zc6CHvdA84jXxo)_$PN~Cx~;Ltqbvn{X4Qt4gaeF&~%gr9*XIO@jL
zJ~qXVg3gcJCKInHL|gJ*iSc5SH(OsE;9b6K4{rNfl&En#3=%YtSZMzT0r269%3GP8
z#S{5J9^?wC;yJ|1$o`2IR80VFu&K-I?}?I$jpeQ=Ovz^i&4mk-<&TUEU6ENq>sw?9
z#>l4dcFc|3M$^ha$P$oGX1`&;1MG=dl%h>hO^w01K-J_m$mS(7p@oc!lU2FaN>`I}
z&i_4cPSl#=hD6R@btIb>vTLAzpRPbUJ(QQunbLM@$#(iJdn|x9eN2Ej2F8aU7J?=6
z_TSgOpNF#y(EirzUzaBvX5v#UF4kJ<&_q-UH)_yF3!iI5J-nrkk|Ncm*@tpGkav#_
z-^vioNIUjOis^2L@TZk+N#wmS1f#mK|ISFI8XpI*ZXD3R5qm9~P-UD$%<9r2eAlt=
z*P<K5T0In?gPO|(*Oqtmdyt?}*&=C4xY8_>3c-vWi#k#U+IUtibhpcWJc2pM??de}
znHe7sUke)S?7cc@+=*(qLkYT3K_)J7X4Mm_-m6+<Z(k<b2trlNP^n!oQcactJ3}CC
z1;R?Lq+#?`3Uh9(Ll^eWIp*M)-MOxzehthJbfKh8=oC&r0m1=Dqs=kESBT>^%f=&E
z?*&|&DBs|Oo{;)I=J;uTxnT()a~2)ZGJ1B0WIdj=FtJ2)X>4>v^9`G(x?^i@XD%?F
zjZ!{sm?^(>qFUe_<lilA%>=Ts<Fn5?lHJ?EAhn}nK2hwHWwm37VbjW=H9r^5>^z(y
z-h#sbZlRk{$Bhj&Fe^s{c|i%bh!9x)@Wu7HmUQrwkhXmN^M1CIAVuxVkovE%q_3_i
zPc-`Ev02?8v7+}-<my;SNel*R%yJG)P@tGal3+}E70B2o589{f$DU@%*NmPF3e#^S
zT|xLuC-ZEb7AvxH8FU(R{-lTBNlivcA(i+pR5bfu^$z5*E75<W4qf<Gbt<sUP<IAU
zQ5Y8PTk1JSz3f-Y6LAX8#0j<MLii<!mL|Pc-+wY5DY4-{Zbbv4Y!EjR>Z@fE)A=d|
zJ({7m!h;)xwS*p@{Sb~+4WtpcY`4X`0?W(30NU|2#a%;gr#2dE6WOSAt4W5m<9iq=
z=2<@|X?6E$o6m%aUC1_-Y<%a5`pUxF_}>jy!|CniDwE&F+Q58wNrd1*t!zvQ6V^a(
zf-iQ(TJ`IiZus48?EYX9zY>^9`RPF#(EChq_Z4PraagaFv!+I+<v~-~rb}@}`A#>b
z5?xQ2Cw+HbP6`tc`ZdH*yX8Rp-_<z*uu<3eq&D*vuLaHIUPb*_uQkjVudz8QNaj$)
zZjL@;u{0dvdSx*UxMOHwh!HTO*pRC)oR7Nc*9P`od)zI)NG)t{_GCljiES4{n|nr;
zvU^Q{p8xgdMbzBkEKc~^D2iDIls7}TZ)X&tXAutG)E9zzua&<2>!#<joU0$n6f-j!
zX^6%nSe0Dpfhj77u|K*jFdPreQq<IBaAuC#a=*=AV`64w3*+bZ0HVJO&Cj6=K&?L}
z#^l~I4ak6*B*%c}3r}6pW71&H$RV8WiDrg76*H-8Yo{zE8q7r+Bn2gX>r=e8t6fO#
zb$2Fi#_#UAqiVH`b;Wf^@j;YcoLtQMH8vm&M0VF;b3v{y@gC<rhCe)Aorw<+(kcT-
zT$Gas$d;wht<fyFT=ni^wXD+zgRljaG-^V&w}_-;zuE{fIiYoit;%oWPovxPnPQ#f
zzjHk}?LF}5lGAdGKCxquqU+ZXpk+s%KVxpnDmUMHp=DU&ad*o%Ecg^~7}4#&MOmU#
zvSO3)!eKD`mv_X|TGdXTZpQ->tN^NW^l=Z)WBN=WUF7Ce3B{4is#9WlwlyTjwIzeG
zs**;|vwz;d#hn$R#PJxLi~*oWo?&7eaEm72^%RtJBXm5%Q}$-1{Cv;GnU#4;*$DP`
znv<F(1nSF8vdrwR0rGc7ow!?)J~`89k}T)uJw5Z@O92s!hgK_+dAJC$^t3E%FA1h=
zzEV!#9lp%Esh%yU3Feh6cyv11)pdA<(7oP{3i`tSR(9ac3YIhY`>OoNEz}0>9@)W$
ziAoBEl4=+u14~QCp;RjY;aDV`D1GIe^W;32(dn$MTCb7qC57NtsXGfb5q<NGXB40D
zt4|`<HAzzkPU<TdnTEc&SWy+CA;K!vdku}JZ#Ufd|9rp6%6)^>el7EqurHM!=kENy
zO5Z{Kfe3jW_lra=$7wO6!7nsuR%C1zGgb!FL{G7(i6A({RFFmcirpy=@%`<x8lJ8`
z`7fu~xcpuUL%C%QZygGE0W8IHjDwHezDe6{E9-bU@>5sX&EGKZ*{TV#R~cz(1+f$<
zr-?$hJ3P)uYo9X*%Q}0&R4gsnMM+GHDJT+^1Hc9q5h}2hhP><kNgZN~;LuH(2a?zw
znIP{du6H}&QqxPrUk=YHj!3Qf;C}m;yIZH~1G92Wrp_Lp`%U$Ld+8JW>LTUzsq#qN
zX`-GJkJ!cK=;%c!3hW)(DzT<d7PD?bxN=b5&9n^13^2<zGx{^(=d8X;Xk1Nrax!hv
zmug-5HwkUE5A50lqmS1n7rQWve||e#3y+AzZ$z(3O0p;BUbi52IQVZqbwYuQ`sj!*
zEzY~+8aG-mvcU{3^L<6OY!!D}wAu6awQRomxiJ!69C&?#czyFuR(o}X<CLt0uVGG(
zbeS0>ESA0Y&jS@MUvOcXwX834Q2i&Tf)~hVF(c6%!<zN&>hJdU^x$3QOKdTNzq`}O
zVO6elYH-U4cu6ndqn}l%Q6?jB&=4zkIG|Jaf!<Oa@Rf`BiFjLN0=@3nOZGe#`TGH!
z2CM$OVl9@YTED|TSei}-WDx1ZUuc&*l@a{I>tk?(E_+Xt8ix}&mKmWQKPvnLB&!uC
z$MAiz%B>c4nr25OX|1nBypJ$CG$2*&rfJVvS7FuqaHaa{mlg#@)e$HK`!T>OZ9Wmg
z5QA}$_&4M!O<j`Oz2}qZ@wnQ9SOg6{tE|>t%Z&0G24D_a!JHHx(~&uoEOmhhg=U2n
zE~OmfGAtGDFgdqfER&InAy92NI~^Y@`A9~+oy|uIsp|KdP&tl_j?Zi4#*6x5RT1nm
z>rXIX2vgx(ep~*B?*$?1032d4NO`qiGdtNEeo6%85Zcb3(DL>Z0e7`rorc7jb(`9}
z{x4ej&UNXye#Kh#Jt+3m0z-yp=}*Jb;m3LlkIDO+?{4e+;<?2IXZ0<_vmXEjX<s7b
zQy?hbt(CVIzk23Mt)H%6yM;$C^F8xd^#ZaV=V-t>ixT-&U!ZAbW0f!$1Qk%s5tyzw
ztSZ!urX-WJ)x734nUfFMhyT$0*5AAe$Wk%aUYn%2ER~MR2N`Rp+=Ee=>`0F7mg(I2
z)%e7jOix^_gKP)w_i0f``(Qq;+(-P}yV-pGbaf=W7{Pyei`})=Dm7y82=eN5DsvMx
za^n7E6I5LE{BaGypLx}k5sZsVkdzFR`_<wc^L%0ktUK*&0sp`RLzf?=yIS?qFg@|R
z$RckGldz@bEqH-aPO(vsS_yYP`f_r|^JXla<Jl(n5SL)EVm7lxyZam-Db%{i+G>iT
zS-q$Hb{vmI>QaIrzEM5gANGum&ytSGhlGIJPeOol#4pER*FgxG$)A<_tZ8XNnM+C#
zR;>gW?D2Z@FfCI*WS|Y-SivSd;X4sj_Px0NW4l+H9p+&a4oZ})TgAl*jZ8YnQc|$X
zk*wdt+vqSfW`C3}OspMD$@Df@kl-^A$;=6@hKWv0qj<O|oEi$ug?vCoeLvu#Hd<+o
z>uI=nR&MD#)nYXQM=R19#>BIUaUo_e4C^#DhAa=LPl*KF&SxS)jZMyQu6~)-V4`rE
z%i~t)5`Z*msC}O$6&CVbUS!~`dK@(8P<UHYn$w85p%tQ6axz19g!PT@5hXs`&Z(?K
zDsH;-is{P`kbLDY9v(x=CIZH`xx4jlxv5Hu;cyx$Sr7q1-0o1Rr}k{RO2MDaY;U=*
zbb)^4PhmSk*xTY{W0_n#yzC4~ahwNw<A%Q90g;l6AI0HlI2njUr*z{8Q*BoxmjC)4
zi|9FpK*`E21*wD~m}KVs)%`}~)teqp;Xw;j$;r{Uuzn(F{wY_XI1aJzD=9DpMGxjU
z8p-VXIJvC<ypxpv6v+~MLF^eIE0n9cSE*VcV@5IJiR<sernNH>AR>Gn&z7_GaPr!V
zYH$9N%bZ{gltIZ%ztrT6<F_CTuN?2SAak!u*c#crHi=pUXlBja3nqa&V|s-zsP*zq
zJYiyDi)mk{9msueY&Kq^^J%h4H;9&ydqxEER)?IR)N*wFhck)01$Gn`lvgnt10Nwl
zL?^scSYbbL6Iqz+w2^EJ=3&rBusJ_uTGf6v+a8h}aV@I;^D#2BSDBu4p?B}iTH61g
zVNIqRF0d*_5Ut^d4KELbWeEB@GJ{X39+8;HoRjR3@Xx4uD?2e0!2L-6O0zYofl-1i
z$qH4Cd!1-Ts&T$*FpU!@F$FkgEE%f%%axi<5!vvCuTzeOMZ##VW*pF$r<=tc0SGe1
zR=H$Bu~xvvRUy!~sCogK05QuxVeeyivi)^UJCi0g*PalpJN8(@+o(UW06lkNS{+=@
zU~5+Ex<j4@AWmM!Jmb{-6h?0VE+|ANwCCjJGJduVLn7w0)kOlzdiDl@9mZ(FSC!)R
z8{t%0f|`?s29snl(`D!m#p$jsfZ34G&x8(t{n^C^1BSztWd99};$}>Q=-Y=X6J+<S
zK7DB@pJUCgmh=9lVasaXG7qBMEIXKjapb@_Poi1Z7CZY3eUh~one%G6Y0nwpTNl_K
z0bE1R;ZiH_Zo%;}&0DKz(6pm3?iPL*%8zPxVqQL6`z$_}RPOKoTsqL&a$dkBA~u!X
zPO(dtb&+$&uhJHyh0iIRDfqTAC{Swkm-n+6OtuF30pSfY@@bwIspCi|6^6nuZCZuX
zPoNgxT`X=JoXe595=LwIZEM*MRUz`efRC?`Az!nS%{REVlqCkjG5n6hOCfUM*$%*g
zCfl%#iGLALU*{I@bzy{NkP*h9sKjJo=-?QPp>3~51BsS8*zq_PrK6=mrVoF*5^mvL
z*Xh~95N4I5)|0a0d@hoUbGTflyF5>K2+83OJvRp9I7NYcMhXrAz~TC>+(%N60=hn!
z7drok85I8#clvnNys@gdXTA`Y7_9ibc7ka%!ROjf5IQ&JOV8brLLtX$gT2pUGEF50
z<sg%eVTp8HApWo0q)l-dv1%x$WBj$wjvjHRyg)1nU2xlBP3|+I5dqbF`1pNj!-ef^
zt~vwZm;A&TvaRPisNUQr>p~nqzi(OD575k!U<REz*iJ*B@$c*D`aGCyT!Jqj4>gI*
z+n+BfPx6#?8z<Vqz#T6afRm(M@hs2-&=h^LeMATz3h|g94v1lJE{mDUe7|pWSP}IN
zKBJ|txu|MNl!%$qBki|BsBjky_QgVV%f(8V{dzKA-;4{HF8ZFpeto}>TwX>=Ua9eA
zr0A)&?fQ8WtXaN~fN7=a8OE1u@kbk}ipo*!y|HY8U*4BWRXV);G~ZBXSM?$(xds&P
zr43=)+O_r6(>Bh8Cr$VOUuAgk2y?yu%@ad~Uv0x%&7UGEo9?vT0a7F?^;Znbf46Si
zGOz@$L`-zW64}PglhhY^x4A|};7#eE)dR|4tbv`f7BNW8fqdY-u>OOewz7bD{HM1N
z{`TM*9+r7~t~u3S0r5f_=ckn$Fk!Bk<-Imx)9=&r``R~D<evlzgA3@>I)=mfV&#?h
z+NnPqMin^OZcF5t3G@<?M?Y6r)qF*}Wkw&t&TRhrhO$nU?(ErT#YT@t*?r3As@9n+
z!?x!C9&Wi=c*L*Zk?%K;rU-65!OZ@olp`1YHhGx?05xTWqB44PpE?mt6)cw8%msRt
zNXQQ7s{4DmY)kk)11ow>6XP{pDadsou2@c9vq_A&bM}#QtypgiC(hi^=w6xCD}K-&
z>PA~M^!CNhD!v*Zp3c%7`@n@^++<_qE*XHgob52pC2UE=dw-|Y5b2PM%<Gq!v}Mvb
z{K03YlCr)gyOqtSx_R%{hbM$reS{(QxlxG19Nk@j4f&7Y<+|gQH;fvD-7%a4qv-+x
z2@kLj6agVR%KMps1Paemw>cBES}J}=+dkcHdr_KYd)SIzZ{fT9xwen-W$)@mNvC9E
zb8sYq`(4(`+0>?2mK>+ehT!ztJ`q<`sEvo9nh}eLc7oNgCWY%hr-{>hQRkbp$#L@_
z;;Z4loL5PBab{A|hE>c$E05~m@sSuq`^)v)G0v?d2-=sjt9K{Yf_T&L)QLJ9&4SkZ
zvj#aEvks2vrJ9vKMP8?GQWw|OD*xSC5S80^3{0RHIRavus9?~1bpCby!1W9?;5bqJ
znwfjT*MOQ2TIL-wky}D@`3m0}t6{8~&k1PJA{kk%OX(z3C(PoTfq>Jt6&v!<HQElK
zX}d6i>d>GA5f1o2TKJ&_B~2QkpRUXkPWu-=EeM}!)lD;Epg{eW`4Z-Bw`a|o?6K|+
zdAH=u3=d^We=~bB5!o<3LA9sDJcEri5>_*F>Q)gV!aYEwqYIbJV-q2?a+lya#ymEh
zIT%g;pvrWzF<{r@e0rp+K@w3^GYu86YHs{@HPD?$E;`uJ5gr-2ckBtecm#pgN<k->
zpQ}xT@ePoL4n5fbEpM6t2YEY^K1LJNZ%;5XyR^VG(DEPSJ+Ei3>)|5`B>OC~A`?m+
zhb-Q~YAl*I?fAAkMK$^9zr7p)Ts0xdZj6=dU-j^Dl+t4dvx-DZZH3&B8>da+ZDWXp
zDy(F)(ynr83xQoZ(+YBW9?Q9seKGWn2y|o3DodbbrE@M(E0b!v{43~eH@0)!kvj>x
zrT+pNr>D2L*7s^ws{uX;l}@_qAlm$fgUW9sYJ+%5f9GO{J>Dka7S__JFK10&SY#OW
zsgQ7=zy}`=Dl6J_W@)<92}BRLq0qi&327y9jw<}VUmQD~%1p%KTzuwV@X#3;!SE@A
z5|+?-BPDYr-3^P_Bj<9%;kn$te5MxABY!4;*aKb+_RXspyd_Og#N8n5N^(A_(^CuU
zt96`<)0D<z)k0SGd28l7wMaN%1aT1ag8g>yjATIVzt^nwlcfh}N+MFtE_zWHv!`&N
zf7{J=kVIITc6SzkRHR4WGU46G<|Bzakjc46As=b6d`_ppVahBcH8@brE}K}h!0^b^
ztca^fT>=!#3nGcRmmQT7qiI<aAuGPWhgW^iEr!eM2#k{jt=MTf8z4D+=40|C5X;Pr
zq>0veG9GJ_?$sM4Sky}a_DZuNpv6!~u8ZnI`i9IQi$elH5Y)=iT&=s7)@EZ=Cnqav
zl#eYh##6m7HnS8-2JTQt8!~fa1YH>5&Vz@#p1ATGZGc0Sk5qO*j8T0LBQ?4JlnpI1
ze6d$^MRB4rLFG2&?U5lPgMSmQf|?ptW9U9Lp_Xl)CK!#~E}oHaBTC89(*PUmWeQky
z1i<cx+D7k(TmSZWIRkh)*7AzXwBFvTcG*4OU-i^-L5GMyVt1}EMGPpo$gV@{^0E}9
zJS_r21^aqsJ6L&BzB#4X1Q#R`Y*GOreCe}^V`Sz8QAg!DF5bPPXt)z`-irp8>6bSQ
z4<88?JOqg^4GK4JFA@=ck!x!S+9wZs#NbLhct4D$kLvPR?Xd;t#@GtP_JxDTyf2?J
zpcjshy%N@n_BGBcvNVM}T*#`Pvz3k)c6Crh7-k~8-v6$5BW4bZwN!4+%^U-Z=J95p
z(jX?A79l=<y?t=g;Spm_^L}}h0>QkiqA{ylrNl-Fko~pew|TN)a8--|o`pz64Iq4_
zI4wU_ueH`g&5<8BBlB4QTS{=eAbo<E;s9x#%nDFu2OWbGT-8Dib?`-F2ne&Azwi~Z
z$M4S&6umIZBtJhe?~TwUPXNsw`6sHyny-PN0S31bvXpGyNCdm@J{@@`8ujzDIK<`%
z_Y5AZG`}M@vvWy{t7zZl>-#nb{<gaTVvHw0N<MkkO1%cNuSqQfxrMU<+$M}VMWa~`
z-<_jCn*t+aLWwgQGqNG*bBU@FsyhjqzKt%5%iD~a^qmnw#1^PBGDi%%Y=ax(E2BWm
z))N^+(Djf4DM;r|Hs2gk#vkz8s?acA4J`x{P5i#6XjKF{I#U5Z9(5zk%y}$E)Lba+
zf0BLcE3@38_Guqx=km8K0~{Tb11TUZu^>kn{)XKIAF!9<B4+NHd`I&WFfkdS6V!%O
zZ7J5sX~6-5ocB?(zrFF2OS(3K**S89)W}Gp`s&%pyjA-9PR1QWj==D)lq^(1{q)2z
zRt@a>B`C-c10PLl06|&U<K7sy(FaeI?wxw8DgLm_`v}A=8|`zwv0=52eGV8ZSI_pc
zEM^_b`}t-iC9zx_6chr;tcQMcSr5TbByUsE4hgr0L=6k=j{ldT+S=T=Gz|)ZsU}&P
zwx+Db#YQUjqh-W+dg0ohT``^4XNE~G3~z|D_G|iTlWVLLOUpR++ApUW#(9Omvfmz4
zD6iO&93?oa&pZ39j(R}%A`Ntu>SV=UiFD-Y0&4<f4HXTHj+V2ywu2#SJ{zggggQ>D
z+b}Vm3!^%_x3`;U&MU!_Fu=_zZ<Aq&@G4Ezm^cq;7Wm^@33P*<ARV;l1TRpmSfP{l
zhz)8+G=9JF0zSf>39l~5s4xIQry2P%Q2J%7tOe|+A;%=A0^yQ0<D(T&pN^u+!DiDb
z=dgVAdn|B9v<kdPpP`;;(DmlKZYrQbVLb;^3n&EAY5S9f0GMR8lhQp54CWWODp4Oj
zt<PhHJGah_(FtVU4}9C0VmI%5<5Kxw19k_Lt&nDOOYNN~^^4L@ly|#W0gTbo6ev~?
z-;B>O(gv(%S5py%w_R<4ihkwEt6>SoM;SK+z+(zO0n}!L>udTyXHI<iT=D#_HqR&6
z28gbDgb;_}G@b1VP$-heatv%oz+UJ;ZAU48cIvuA@LKz2Efj}J-=oE*n2fMsLaK@t
z2w+-bpd*YaEuGcHm{>St1>}Js+Vo8D6EQSqapK7VbqzUC{UDueta&)~cp~bE0yeAp
zKzouTvzAv&NhAGl-Vq3TX6VL$nYCOZii76-2unbQo;eYf?a%l<sDPk95ma<fqqzr!
zzQ_shVKFiU-p&UFNoQT%qW4JBr&5Viz@uD{94{!F(tt=|lm0qZ{9uVMV?8ZI>QezJ
z9~>j3{R0S*A*k~l?;x9xn|02aNID4gsrzjg-xu&%cxMFk0{jdsnDc+~_l4hoLBje{
zj8;Qa`IqOr7pzMs;CJtRU;41Fqni`&y;mzeXslGpvNi_@*6Ueei8Bq?LYMoUl(6m*
zTCS5p;s{m9KQ~Q~ZdC8hNY1j(Y%wh?0HNU2SZ}ujhARgX>BiST;ts(He!l8cARsVG
zj82U_noJRE7X$t{xeB>Dag-el^#aUKLd4O)_F5wppmhyq(TTaBwpl2XPcJ%5R-{v$
zwOUl)J0gJufe!UkbJPnA$DVVVADghdOq1Oivr+1Giv-{Yg$1V@US~kR1(}0OWX|rc
zJ+a`rh4Y^#z2j4*Pu;-z26W>#&D}f#{&$x~Sq>(j+M6UN7o|9+%MuQxYqpWK{}*2`
ztH&6h@F?y8oza9=hNUS88Lc%_#ae>uNxJLWphzckyGF>!(iKY!`kzoD92u;?Gnoo@
zAZB>s=6MBMD<yuGf926HOAe_Dx5;dr_`Nv%M=lnhZGbc9{qJg!l6SE)V2zB>TF*(E
z0=eh?p7u*{8(~u1Af!FLhW;l?11i7_zIXG*pon_Y^yV3)O8$i{hLjgL6GE%uFZ#-)
zq@rJu*UYj3u^HWdhl2mzT$O741WV?9BLU;T{ix+zxoY^e-pP!yNTVtS0LK`OWj>-y
zk)5z5@L5m+wYBmI5A{*rXLfPsrC`ZVO`T4?${GLU!Rh-2h)a;HAP$@o7@#z;VqisS
z%z>m(N^1OLK9A@87_=qRac+VxV(lG}d5zx1y%rO@EO+Zp50-x}Q<lq$!c<iK3$|7I
zqDaO3IV%Ko>#?_cmxNn`*2o>NpDN1Bzjy+{+MpP}BH}+OtU%ojM>^;{=^pv`ypd#x
zju;oSA+(Ch`JYy=Hm{xhYR>D!-aCIjEpTU{qf;a{*^01WJ|pFQ0(Z?TNL>L%4=Q?T
z5<|mUH%<082~$o5q(V}(7~nIRSFOcFgomSAGyxzC$Yf6V^aLq9Oud8<w%vg=ls|zD
zRhqHINv25wYC_Y`g_^Vmi&yU~8)5zp7uR-*?DW(?(0qD|<Z}JXatR6=t3Wp;J=N}k
zfCKg;khONKgIaan`%AqZ$+I-@t?DL(m_;@Fpjw~9A0&YjdV&t<<DS^DRFtD+QNruG
zZSrq`KsMT0@h3<-z833Nn-y>bdLBnV8ME>Egi%@8BJ_8Yn(MHfIa6o9%62TkBhu?y
zMWbvy@1-hH?n+BDyh?nqcj!C42SnSl_SA_VjB~G{I6nKv_ZI4iny62#<iL;sN!>|^
zV5Z|0pY4geYd-%yOzGj#_j;?$GQQH&^f}Z9Z1*WtL)`Yr^F|ASL~-fq^m%E<d^}HF
zT_AR?fEzki{tKOs<sX=t7PA(o2hBJQGW(=1p|@JQY|Jd0tY_Q93{O`=#~nKxj{1@v
z7nvtDo$D6dL5&IdBrJSt7dV7+c?@&at-ocLs%Nc<enBvYc^t}jJ?~dRR|<Ppr^DWx
z_gN?V=Ggmsr-6F4uwozrMKcfWe9$z1<T3`pQGfi)%1~BR$AiqpVV1$$-5$@T#BGxv
z_z|Xb+2U&MWnVRE(&TMj3fuJgDb6)SUtf=gB8@`8t^RAWT}Z>_T!rny{rS~j4n;7r
zqwR7xtW`Z6RV?iehB@ZZ+o&&1i45!wx4x(RzW;>zb-$62VON=sGrduCl>~<wiswBj
zawSu}Q|`BGr)ve~FVT$d%Uloi^{5tc2n&k_Vw}LEyrV6Gk_z3uJGgd2b=EwrM3}Gk
z>PwQAqY%r>`Y3NBIb<4ohz^gmxvq(|L55s=fb<s^7iyTMczdAlKwwXJIq<16TYulN
z9X#B#J#KffTnQ)n2kw}f;=aOc2fhz}X}s#1+OP_-q1dpt%mmbHVJeuA_mwM-To(#1
zobi-ZqjJdU9kjKL{dp_=M<T~d%2SOO+9G)7|LW+upfCNU7b5lC4K>BdqGR<^PC);$
zmp;`k9dnPoUKZUXYKx>(&Q(pjQloryC%-&=AMc~fs_n=q4L6u$*sg0nk&<3Ll2pnX
z@>$C?aFss4Igd8N-mbT2j8WNl7D+-_a>O?{S7av`fzlo?*$?#d6zNzWn2%h{h9TLY
zwe9XqEQ=Ohe*&LPxqd&|Txf0H#xg9x^}MvOt6e><gZ4;kQLDzOThU~Dft+|LO(8b8
z&^?4j15sGE5Gf>~fjg*KV_X)*Jt<F}6z7gx^22-M^$PM5ldyYyaz1@<$GI!bRx?$k
zTwkSWx2ZFwd_&t6CAwht7yzC{JISDxh}^3vLAZWJ^ri4hZ;uZ~lr&vvXnX9boFhH}
zU#44X6gxW`=KxCa+gp}9r!t&gf;x3QllRiQ;X9%we5cF9spHk%P*)P4MZ&GDTevuo
z>(z$zZEzmncmGWd6Cx#Eq<E<X{w3DQWR*mw@LnjQ8AT6|J}NpE{5n7uZ3EiQIXh=<
z+~+7mgq{ocyNeYZVZZC{GIcfp>}c^p*!g>`KfM9Mx1FvF+Ijy}fH%rTIIdYOds_uM
ztzCA#@#X4d|2cLf7tVFp58oV0lj-1W*@dCFUDGu^oHAJ5y^uL;?^S#}$w7kwYnYa3
zthRc-{5{w!n0|<0lkJxNXSsC=V_C)oKTA;fh;h_${m?sMr)hBSO81!api?lM-v8?{
zI_y4I59#uEE?*v+(8H!SVlMY?c1`ixI`k!4ZKjCu=I^IJ&EGwqOFdr^3JD*ig1Hs^
zU{>tleu6XkDhG=S$JXlA;0I^o$?)?hZwFb}sXcJ%f8Y}z^w+<l_6Yyn(_2e;+1uNd
zx@yM0;;u=`NS)er-NSsmUu#nw9!>YvenB+>_EqZf(Ls+$&-=GW^|fQs%=^L&%;Ccf
z%(f<1ZR|=}JUO0N*H>Bp8Ud3_wVAw0a>08eQX8Qk;rA4+GaQg8lAB{{f4zLhb)3WA
z>~F<>1qfFv9lY-ZRA{KLyPQ&Daa9f5-d<8ELK_Z+j&U#jT#vQ2p%>jHB9zY^4BMC|
zZP-V+6`>lhSBOkH*cSy4HhQs(a~j@5r*dTC(>)@KqavdqNjtC~y|IjL^<5W<2QgN@
z4Zltpt!i=fa<Hg(yPR}-NQ+OndY&9(u03AE;YyLcR18O)r?VU{a++~ir%DhX`J1B*
z4vrraFTL(-ds`ktF1RiDdZm%UN&;dCOib8<=of+-FuPE8MBj9ps=}$oxBZ<<SZBN0
z`V)}?Lj7yw`dW)#LdhEo#Q5G&^OqxhVVujHdixJ9jR)MEPXlr?@20<Q-9+9$Cb>*1
zZ<<)asEzsqPOE2iUU6>5Un0zlZRD41W|<cKD?lI8w|Y^}4<2$*eMOP>#j2UR@{Dgb
zhJ4oSz%tF%Px!fQwAnQ0=hr_HcuWXv@lCo^HUTu%N7LtpEq+LkR#xW5p`!{{4xW!D
zwQQ(!e8JC&@VXu=VdBm(n~=p&XoqkZZg!wyTTsDfk74nLvF4|OT$JeHTs@K4-Pi7!
zh&OpMP$Dhbh5^CLD?9AOP*FpXKC%s@8cZm35!+Jl?NGF(!eVDN6J#VWeZ@S}K&*2*
zKf+?S1G3VL`&ZIza!6BreYCj&ao~47AkwVe0SWAz>|TSBov%SN5e;9W5#L&r;t`7g
z=i;toX?<sG_~U*&yHZcDe~Co=-R%Ww{M}>z$=)Y~U{}S)hQD=F_w0*(`NKezs~Nyw
z)BzWjLZtEL1VR(Ul5-J^z`QFK@o*)!Sz5_$C`48RB@4gtBRX`sr_cCdtWyQ^nQc!x
zP$n8c%v5w6?7PdqQ^>v6_VSaWMVPj}ehcGNNa;M&4$T;xg!__*r66NQq<Nouq?tdo
z+0&KUj?zB9FID>B!K+c%&r~Y{?>g37>p9E2Hh|hiC~@V_+O3Ety8GMXZ(4c<jpBgq
zoA&qmBNy=9;H~=!!J#TC*iISlxfao*)oQ+6C#?faE?E6u^{BS%6W1sz?D@vI#ocJ6
z*;%p*0fpQeyNjge{alk@jq`UADHKg_JE>Ou$6L4`q|WSg&#_Yz%4>I2aC>X38rFJX
z`B5c(brRVqX1AM!`4Ylc=c(pTU!UlyH_mjcnR9&KxGWtag{u9^DPbOhp<0@57p*hg
zk5K5mBsVZ8JDKd*ZKdQsY5;V0B}p#ZTWl!@V=0VpKr40ikGHE7aLlQ!2`RPF`NQ9U
zlUVTPRK4qsA5Rbzs(A=Pg!aX(4!U;7)%#3@&foCp=3*{l5AT9D=Ei60m!*dgQm&T0
zgF(*QzgOW08zoEHx8J~_x^jNAIG6e+KxlF?)#=rKU25!6ZzEHLP0|(TKZt&tfBAy`
z8-N^MOTD}PS}S9JPWSd0tBDp`)v~pGOBegtWVN-OeemSqo(TC3cO&9tbGQLGo7g{G
z2Mn}W>X0|-yUA1~)YrRLuH^@VD=FYr!G+M6A}_74ZUPO-R>17~ZiUUKKL^H!x6WM`
zMY}1(AWpC<c3hr*Tm(ug&^XEI0xjPXW>-6rbA`FZpXX;DemJ>(Z$q!{>pi{``aA7U
z$i7ksbWE+yqJWF#lXTN{@R|brZVw|du2(GKTm$E%lb6=2bSnZmPKDGfDc6b)ADOAK
z<|3PW{n~pkSSe#9h)LHlFYLZKSG*?P`ZFppvva+Ouhj{C)2p*__ZR%QF97txe&YQ2
z`YPfJKhD%SJiR0M>dcNa{0K5h?tZZ(a2&xR&vzC6aiK(Y<po_{-I6b<vUl@pg!Bgh
z(XYPUBgseu9A3Zb1(%(nA5UKbda@c3w-Ms6vi<%X|0&>X`e~x6oCAB??LP?FL7-d9
zZ=?t!U<LN&W$!-uo6T)igCtA1+Ip#_+yKRLGHA29JVCT&*VevS#gp385?VoUSDuGk
z<pB*!8eq;dzcCvHrToAlU}Krn*i!<!YSv>@YL<;>s(a3Npv4F#8ue&ij`c^SRwbAI
zi~aF)sm@E)wFVe{SpDZ$+E^J(!W%*EF!dhfS~-7XfQx4wXm|_e-zO#YbrM+m@iNnD
zv56$LjlA)ZoZjeg5D;3vL%}C)>KRe>!Lh#;K<#C^Xwvq~p?1oq9CVy@LiIX#Loc-{
z&7FHwztXVjxE=ZI0{-DKc${98?I3Wa%L!RU0S^T}jOTo!L;)DRD<I@<HBrP<>L;28
zD{1HFD3GOX0&)PD_INu>_nGDI(G+<d0gqO*wX;x<YtjGsjn{1)WOVm|O?=9;X3p>@
zYb)T-C!cCU$93uK0MRj5(enk0mTB66+X*O(H7J*xZCT#{v$n15(@7z~$m0!d!TN>M
zOX91*`;uO6>;QkwZK_nyl)z814%Do2KTKDA{j>!P(lvo46pCwBWFVq4FaI8x4qc&0
z`*cxO=QDjk9PsmO7C?^;f$j#(iTZ}i=$ghp*-Ob(;W(_c{<oD=Aj}!Cl!lAgy8u+)
zB4J}xHvrWhn2-qe0t-h~7=4L<8Td%14z%l17ay_swESJXyZuahH}&@p{G{K}<Cwu2
z!1;7j69<pHl_PxxI7}+it^hZ$a7?z^ccBV@hmVed+^t|l#AJkKkKq?NL_3}jAEie_
zIo%0tUjokmWAR|8-`<k|?Vp|EDNj~r0KuZZ)U^yuO;t>k)Oh3_APgFIxlAv^|JVjY
zIV=}|7lsh;0Y9L7Mw-4D%3cPBXOSr4bu<@cNo~4;tq~26CP(AcxAS>#>T&da^mt9V
ze@odh-SfVBAJG@L!GEX!nq&jDrs)HQUD;eD|Ne7$3IY;Yh5i*jeC4nOp7Bp}Jj;G(
z>>QA**~Q!JEfXTB?F)5>38N9qUr${E-<Lr6u227hMu?hl4$<&1FmHjAke@~@>7@FK
zoN&ksal94PYo#d?_-!{1keRH<vkpuk)Fpk0{j|=3wOBZq?SRpkUm;sw{6`*H_
z&#?HiVqY*x$YiR;eur`<<3o2j4!q>Jg={(VT}NdhTS(wDKtu0Dlku(H7-m67`{nCt
zj9y01Lo1Se89cckPr0eUm{Ib5F5P!i^XBB|=Tq;fMtvni@5^-@-T$o+vgK^K!zq(p
zCu%TXrM)W`rYG#u#qp5zej_X{QR~~;`1U_rrSH<cK>Mh$;C3RrHD6zotqy1A@D1<T
z1us|t`MX6Ip{jpe%cvJ}Z_pJG(=+{;{qp^0)S;GA92WJ!o!6eNA4NdMj3)LCZx~vT
zdg#Z@IC~S6hj+peH`m3!uDCX7pPP($zD4@iPZ+*Qv(;ElXP+)P0?UuYK3H?mY@hPT
zJT*wQy9$oFEsoh8hkS-2j-z*sZN7F5lTO?Ci+ckD<1Is%$8DvLirJoA0qIqhIQ{{o
z)ek2Y^y^u}z;4kh-97|n08A)}$3P7dxF(F_*(h<kUpt-ccebr=vv8Xl5ZC4X>TU-V
z?7W^Cu$wLIMzE+K=`Lt~=qs~*WG2<BaSuSpnu|k+!5*70X+%6-lPbQNd|CDuZk+3S
zo|cy9LKf!pxtwLzmfvoV7m)06;FQR}fllfbMzWjPiIz-XexCqg4K@v_xVE--E(k+N
z4F=}m1#rngp;l$vQF2+WK%@Q^=4uaMp5Ri5)br8m!Av>nrcZq+4v7KpR22fY$LCFO
zhE2j{9Oi7q=OMt|O*XUPzM$FtKR7AQM8#Ap+@9c8SZ+?cts#ES_Tn3*N^z}}qU(nJ
zsCK>f2*mLRf<#ajMVSALtz{QSIJD!u3j~zFUG6jM0`{Si9m-@YQ*1XN-c9NtAGjY9
zsQR~);W}0WGui0#VR6d;3XKD~6b<T38Qq0tiWXxnT;<4LtZyO@;xWaiyEHx(y8fOc
z0iItPKFrF&-+@PpA8@fb4=Yd>MFDk<|DA4mW>!Dnw;7e=LvJ)&qJYdZP6|SJc`C1F
z>^fI_W>Ve=*SOZ56rihKhyYTDDA4RHSkv&;?t_Sov$nd5N@HJ;@R_R_0}m>o6h-uR
z1J6=XfYZ;72>|XOR-65hMJ(1Y)huC_WWdJ5W^>9Q6nh?6@}{Myr~ldPP`v8xa<cBx
z+8qC^gU#Rm;q%r4ELQ7--0%~WTUr&45=T#&@o+jA`SbpOLVtcBt4IQuxrZYO1}481
zYZ?nh&+Ce*qRgx9?1^;N-YybsJN&VLMW>}4guu^5@8td#2@)8YZdRkw+7EnjUtILq
zb8kQ$DgT6+HdG2!ABjV-uw%isPfT{)xbJjllKu4GP6;}21~UyD8BtU@1@yAq#lHYz
zLIf<Jon8e~_PL}uug9A*>p<Yd>Mubt4sr_CfZwyX*Ve40=b#ro#5<{((FrWxOu69~
zo%qGjhrllj*@ahp$1$!a?1wTlMLEl9ZB^4<=q=#bPS@U+pZU0U4ND(Hqw5H0hc>(S
z?EG_e<K10!U2QD?k{EoFdJE3~UhrPB_YIDi-~eIvC?b%U*EH(c(IR-2NV;xt(=FMz
zJbD2mg6=JW%`tfp?Zab3{0;QUji>z?RSHXO&w*E2y7tn=UtoBb{_e%w#BzjrM5FVh
zWJ{Tk5{vx<q4hNIos`9C0E>UfM;7%11haLO?^7rr+8_6E4|buvqkM!g9tSBejT_sY
zv^JoxZJJp{6QDcB<Ro$3iw&-q@lbfCYw#DMT6b9boFeFG9^@8Ce?4c=oG4i^P~$lO
zKG@?bzw(3>mg5|Of7{*~IMgFSe?#*z4n<+|0~DoR%VAm!CGknFz{N&^F$ea3p^+Cj
zf1b3JNz6ygX-ahfKgw00k5J0;!k>&EgbIOWbyU7nT>ltl`ZoqJ_5KfBW*K(H5kzUs
z$}3h$VtK~TL402mV2lVDNJMkl`o)Yk-l#)9RDTIRS9dRaGYiUx`}%6}ZbISd+|+_?
zFDtTZ3FY%{$!Vfyzc-^oN?P@8n5dOwqNR&^XW@GB?k2TZ(mQA@DT!=PO;;-@pWt_<
zP}Xmmy6z{B*Nou`^DzC(o9!RlvE>nP7PXxX02>~9Mu_*H?E|4$n+;gnlJ|D@?MY}}
zdTD*yj|<E72%u@jLlFJic^bTeGM?TCRs|N%zrtW!QWJ&gJrBf{v4;OH(=r?8p?J$^
zUd=}jd=`yd1dqI0Fn$*bdz};A@?lZipb*b8@mykpI3Q7E*4m<6TLu-kq3GXR61obx
zT`IpBr4?jQHml^%^UB@Pm=8zpw_dH>$9TyZ#iGjnRm>9FK|BwuOmb=GK+t@^M|5+&
z;nAGfe#!D|<_pc!Xe=d;<?jp`v0K0fN?C9jn2nNee=TE}LkFPnt(l#VS%Hl#_~zx@
zph!c$^_tCCS(gg`>HhclR8QtFOp4HNz&wybEjQ}fLxNVrXQZ(E7!R@6n!a6alkpMo
zi4FfcMYsD2px-*cXlL=yv+hn{c4rT>Uq92c?9IhVW{&c(Z;&U@*LMJCTYixJHkN<^
z_zOQUDRYn+pi3mb)-v3_@Ej?-=HJ=PKp-NT3_iM?Q>4W@PztD|MBI*;$UABNxH&?z
zDI&kAGe92;f30;559vDm5!&X%NHl&gnl^((IlnL!%(yfg<KTDV2yDJee%L*-|GoJ*
z*<nWVPVQswb7zQ5>P6PZ9-vUFj&k~*6E^}++#*XbUodg;6B0RY1YA(Q6n*66<i?_|
z)8jp4F0TD>*Ma5V!G~?3Yx`#BS(?-JN$On`C{mclhj+<Gy8}Oxndy)GlG7I;OU%>v
z+#&)QzdDGkR452|$lOmuV2enloUQ8w`@TY|%;D1OKgYSm>#EErL04Eo*;%)Shk+NO
z@D1Yru^kU#>WhJQpd_Q8iH>9S>QrNx2)z&e@ZkraZB#Cbvi|(7z2})y^YkU%ru7XN
zJUyK+O-2>b*yNrlC!I=Afa$V)d0jeaDA@@Um0qS_N<T2Gl|#wdfwd+M;GEM4I*Gaf
ziBJ_;Tc16!PjL~VrWTc@kN5WZ%p}0TApR!QnQR<+Plwm@1P5xwKp78tj~{sa5gijo
zMlSvRDQgB_)MZpqocI!J0ho?r0-0pHn)rQx8iGd#{`T<#+AZGLDtn!_`#i)G8O>cv
zx5!s4N!7S<US$El7ybfw-C?PKg=>@%oz@~(_q4b$oia}Y@tQH{x=yc3nA@ELK_U5>
z?q-ryer~RD#=4Wd4fpGpv5|8@hn&kFXx|ugU=@+^quT+s%f<Uvskq#Cm$ZFOh~J3>
zOIhV@*H1@t@_#K5ky@k#<S=M*sPaAqXWHsRB`KFf1)jK^GLXfXIQxt9_fV3oN4-pH
zrTGeKYa{~PL~Fk=lT$Ac4_Y+;kEOGWi)w4zxQc+3h=4G3cMjbpLraTv3rLrwbO}R)
zfV6<LNJ%#mLn8>1(kb2ft~t+pKAaEdcg}I|*=w)8?(4qp>(7CpN7Q=e4w{RT=W&SQ
zh7*(-yt)jdk<wL0$6BK&hoy!rqsES5ju`p;qep%x7s9Ke@n)-sg;~MzDE?>yoTqzF
z;_i-;;QPKQ6Oc&(QAA}txc{_Vq9}!U-no_rJee)eA~TDCh2}oGj@Lw}j|<E|`Jf?$
z^TGQrAQFy<9qL+aB^tUPa=k2&UL|J0(puK~Jjj@<VbvYFAd`euaiZ3tQLNc;z3@`G
z+3a8F$^sJviNE`d@%JpnNYb^%nz$I9mUvLND<o-589_yInuO76kS}V%n+lC_iHvrM
zh#?W?W9nrJcOvLm>|-Ko`64Bxr_PAh`{A;0u~jD272*YxGT{w!QsbaG&N<<_f@{cx
z<GPFhUg6tUtl*fGfgpgVuA96p<?VL)HoGQtD|_WmPM28PA&RNoQA9r)-0LJln5_d<
zd!Y6tbzicAGV+O$-1})$^nbElZWuowLe%=9QgJ&)0mX3*6!f8O5O<TBN3M^(r@o_p
ztyB#~xZ5$k$cXw9Xq6Jlvgw(N*HQF@Q24EeXIB5v&}8UkbEgF;7mfk@s$@!RkVRlM
z>qVk+#p6*0DX-q1MbEMz&bAv3xiFg(p2df#C~E=gx-hbfbZFdBZ&$Y~jGD<d%_3$>
zWc{PFu|b?>&lgcAP6Pwub&hhr-HGcqKz)AsDvcT(=9f=Y>`r<&7``b+GxbrRGKe}=
z!976=|E8KBAmpDvNQh8Oh&J_MQEIN`O2XBOl%((TeKITqTT^hP6!W=J361KMLRDCU
zZ1KCeN>AGus1<7l^7Om*#Zi9+m!BXbG|}YYNZ#?E^n%15(NzY2kxC1HzBC{NlNE%?
z;aE~-vL}VF$<Yv_!fYN2o2!Wve>m762{Y9aDKWA0B*z|1ZZ0gG<E{@pa-}g-CovOB
zxjk^5R7Oxv%L6tazX&gsxW69YmM0RizrIoSEmW7p&Gkxy!dF9JVhrl=o-ft_3@;pq
zWU=zet-jp<I|>7^HFfh*+lV2aZzO4S!x69wqZT)JM+OhDwhg$X+q<pE6b%4~ZoI9n
zbQJtl@Xx_QRAtFahSRU{0il}j>DQ;hXgCQ-5A}VI>u=DxD#4U5OYFz#DrzyI_!x|}
zE=hOfEs3PRPUg=`dQ_TDPz|DXw9vXLefRINz?_P+P?_6;Lgs%D0n+Wp{oVe(TY>)|
zb*$iW1i&qTt<(;HXw^$iT5uS0aVbRKkkSO`_f?wreybMDH6{Lt%3<xSG=RA8C3A&0
z1C|V^2f#Om->!fs?{%OY5scT#*az&`r8@>Mm@Q=80L`8lLG!^goSWsLDxo`|Lz&c>
zrH&FBNfh*cx9Q;Sns)mS5B_&)ehV-Se4Hi#=`HkQN$Y~Lmg^hvBfq1cwX2Sv10`=`
z6~|713BymtHn>HG|J`L{4B9tl-I};8bz0ch=uh$6`@&lG99!6VERF6v44^&^%K(Ir
z#-KXnO*I4B=7ipX?P9I1jv;p3V!A`40#S$AH#FAt`yJ!^@nm<PVt6zFhYx}vI;8q4
zZz`wv0puB+={wGwLac}WlSV&&cs$IWboa(iJh+hIqT&9_0Lk(DAt%4D=tDDiix}Rb
z77nO<MSSB<C?TM%4efeEB#dI@xrFLI;{?v5TXg_wA20k;-|F>yir#r~itI;1ztLx&
zMob$k(VrLBv@8vC+7g`@&GJtZrv`)Hxf97_0#00<a@}eMH^4g&1(rHEKb{Mw+$Jde
zFj|04LS1MV0x)Ztqh-3)L8b46G{eIhn>_l+@pohUaTJhsULy}ERhmK?adGyw8PbJ8
z)w1H<HW$}s7@8ze=u7;?_ghuWAlL5=pkX}avzu0Hn*@+L2EljWS~0cMt-fgz>1fx*
zV5~JBC2C(L<qR8nNJL&IRqU|h4D^5@Ihb_EV<!fmJoiP<mFVRp%>UZKjvx+}WBeRE
zP5u91E2=2|*Msw(0JG!15T+?Sq4M@0oz9i_`LE1I`E!g!OBR3hugbwK5Y7J&h4`Hn
z8UsBjMuJ5Y3Z-advPm{$?6C1ni?$oI!}_^?qfgb;XTHT7`oyk}CI?R88UfZjgE(1J
zhG9k!t#NV@rK!_1#{Q(D?M=lQ(n)~ngLMWe>A%?QI#~uPXbMu0dnNLZH}lEx>H#<!
ztBd{4a2r>6cl`mWlcL~?QX$h<_ymwbpVwAFipAI$*$#4BR&3p?rNIRU&L9A)a{6{X
zL~dXY#xn{cH(oRIz}<7r9}J6M+v(5taKQ=aW`wI7w|{@G$W!UyN<bsw_AK-QQq>wL
zjwjTkfMBm3BXQ@c4SQYohWdD8MCUvez?6%xRw=nm0{-he16Oy+MhQ}X;s7{4^2>II
zn=V|5B;+R<Nz?`a<gtyM94F2fs3!zOM2?*eluP~7a}v$6Y-<wl15UD%SQ`?(Q_}0y
z$+ZsWSS-UTUx><87A?r&iqRn<Q1q;<tjJ9%A9L(?+N5^fZ(nkI%&>SjUVt?p`Wgc!
znvqy*mS;%e6%OHneFF@RKOipLZIxCG_z+Q(Fbu2#8WC`95#X}eki(2O=WX|sc=1T_
zW&ae&WdoqBWkX8xCtJVd2vYz=OIl9#4b!vgGQZoae(*p3iWwu(bBWZgtiWw8HRkpy
zX9{b>Vky{_bXL)+D*|HVlt8n>A|l(qHiopJ5}I>P2f3PXf`|+1z)rrsTYdxAI-O(b
zvLLoyx;D#?!x?b`T~%2mky;+_OF2(zsY5xvdkuK{<G%nUud3$`7>~*+iq#mtW8H%i
zb`41c1w9K@WUcuJ@GvJsf-lL+YAfyjK*>*21)aZm7cZX!@=D_2N^fcBA&^;0qyvMF
zz!EC$4AA|pWBh?0IiQaOJ0Afe?Ir?<V9rlx!3na|!9%F-yVF1UzALw+z?EM4DXGj(
znaoU>Vq0++>T!Q1iub@1N0Ld~dU!uMbql2D31$kSMP62BZKPc9Z9ZMHmPvgY%(Qp%
z1P(F&Gs6lV6xIJ8RF7A7)eo6d&-kijz)0_~`VJ&*KKD+7NYr~wEG+l<wgRP;60($f
zFE6hnP$!gL6426v4nQ3XgbAw;%zRTJ>8dQ`j?IT*M!orNFi|juLl2bgNI;?4alZ%5
zaks~yM}qqsu%<eLg2rsBu%DLef8>7Nci!&7y?y6*bl%E3CDM!B@}<?l-={@Ey3zJ}
z$NU~cS5qaJ@PF)J85J2R4GJ9Wv(9T6dua2Rm_X9(2m7oyJ70(&r9?EAyX)7-h^)2~
zepy|3v()gHU~?2i_~KoS_z%Yl7W&AyFn+^F^EdoCf}@!{$W?-C{Jz|v0jdR9Q!GqO
zYD8N%!@%D&@DHRDgn)aN0j%&VtPERX7%^!Fz_03=K=+Z5zrDcAYcM~fp2({_adq~u
z^z5SAYUH>1LTQ}3%w|Lv)s`>=T^Hn)$>CRIRpe<vUTtm82>Ijq&NKO3(Lz<he~Y)u
z_`<UR4;%LIa4!sl<~SEwbe)2eZ8$Xo^rm>=rybqN@vGn$><5)F$1A*1b(@$(iJjl&
zrM=U{4X~a*-lw9-QFD&uLX~s|q~$4`;#DqF(-dG-G6P(>TFSub&eW6-6my8_J%G~D
zgE~PB;vznho3@roY656Im2it{YqmU})TQ_P3x<}0blh-@b|kO81eN74fQM?#KaM5l
zmLBQ}z&t|=1xTYmsN;z;En+_sM?SGl<f;p8;2aDFu=Hpnb2~E@yIu6_HOtP(?=txV
z6YW?EImF8C=-0x%zm0-v8bA}#rVHbI6uhfulOtu;5=RXhp@Y2eq>yIWv|tom+#v|b
zb!yX>C%$0FWxU2l^OjVNL5)N0fgmn@PECzg@?*(<lRI)FlqSFw2UIfkpB=1tOxSKo
zCKT788vMjeDT-TQ3KX>}@a~nEb&2<SF_5zuY)Jk2>nBKJGg*@SDNykFlF(eb%QFZj
zy3<odB<V0`ZyJXd4~Yqe7v<LH1fBha>FIrb{atIEEgu#NSJGhlz)-L0?9g_5wyl7*
zuY2<BQ6919mf45r>-9*exgqwiS(On19CYtThltQ4Q!O5CzP(dNf(Ut<B#hqahwk}x
z@OU-PP1BZZ?u5|e$0UknVy<(}z4mW%!3U?rM2nn(R;Xi^!V)#wA3@Ha;dlc&{K6W-
zfW-NCkjt<80jECzvI|Q~qEHqDg^A&Kz@Azk<Z-FJUz&#-!>Eu<>=uDFV%G!kPuUK9
zhYOoK@j&7j6EV{>rsquPOi)jyuD~aGqy{7MsHT#a2WnwKl#txrd^<8Bmwc9&q7vgc
z!g(a3;g`PkMjI`HVUId5Z!})-A(2*uVd}@=SY!8m(>^ymtTQWUyeXtW%_3Vmr87->
z_Fg06m&uwQrdp21Uy4@7UXXWc#bU=|6bboFlH2+7aUjA6v}-kq+Ed5W8USqYQqRi>
z807WR5_GKTxFA{Dv;8Pq6J--IL7igk#@!0q(YHrcM@3!<@*GM6I8O)D5r)AVOyf8|
zgJ6kYHMNloCIKh3U*6N&LR0)N@q>MHN%Bs4RK>@ZA>sSkJ>q#eH_^*~2Oh!bi{yy$
z)>12!o<Agu$hpZe$*J*-Kwn0)v;Z~}5}jsRXKC_&AE-;`x|}$diWumVS@e>7XcdGX
z<CwdeVZtzFWU|7@LgPo!2;T6XA)PJ%EYo;x5?-8P<F6265LMJ06l@fU64<pq|LZrG
z-kFsJNkS({*LvLNiH4&}fa53BJ5Z1A?+QmyA7wF-+TEpwdzfr`f(mgQ5s~HGDep)S
z$`Q&HiEVn~M49tf*1^uN&x$FFi5C7wE#Am|QhR$Z<)mjd5)TfhY~)55Z};j~o~kH$
z_w*YN?$-mozP6*|G{2x=1Ffja+1R1AOStOEul(XyW1H;Iuob+G{Fdu){Fs+}@*+vn
z1qiF{K$a~1<Ux6YBFzgJb6Y>Hd2dW|FNKeCY~|sQrh_Kufj}lUJZy|-L_Ww-^tw%V
zgK5&lC_;y;$h^3hd&=zO0toiwW8^tcEh#WI)O#PD3X%)KL(~MGr%VjNa5T&<A(jos
zOX;$gCHI$r+Qv*EOvfHAKyoZ;+P%v6+bBn>SMM2bj}Rx6lq*A-lP6jblhIq_B0VT7
zC=wCq<BSt3N}7z)GOk6!d$k#wd(fGctoZmBQCPI|DWZBS8p%_0i;e&i$@a`MU?zE;
zNP?;AT>wUN3*XNXWQBw)hQsJgbGS|~zH378j0hl@8s0vF**i}-kNS+LS(q0>v)o=E
zu8XN@c<M;JlU`Di8fxXtk5<9j3WYqf3I2$KMg0_1A^(bPa=6-pC#U<{@>#dKSjJXQ
z!G}$URdx678o<rA!oIvqL=04R^!Qn<RsSX^Znl}2xqVgg0Q>W(&W}A|mDlzA$KY6k
zn-25hzfdl^XfQ2FfYcHq3|g&PM?je%hin<$zGW_Cw!IX;{Ko7Li8on|YGd|i9^saQ
zelcI_4ApCU<c#oEcI$q2KQYwDgkN0ofsx{HNLam<HbxjfQJc0Qcb8(Ee$JG~{tfOH
zuh)TI;?%oN0ytfOJ26vpAPho_t^B><z~p&3rTsxDIw~%XlZ2Zx^<J);RTU(W7h<*m
z`iC`ma(P4N*m;sp6#8@o1{Q^VD|6}6(YE!tgy<X3&@AqV+jeQqSBwCz#r01+h^|4|
zI9_1V)_TiC+ivE`%k~kAfR%-1(5*c@(ds}z5r@>@C-gCsn|gv8$D7G?3hL!(C-o<;
z{scs<aRTi5b*ik(r1(MLl%t>gbUfbZ)eBDK=6-6BB&Y1k)n+Euofj*v=OcCa)*_EI
z9j#Sjw;i&z+LUV^_^}H1E^xWMPhgP+iw#Jrr685`R&~JR2-W=|>06O01J~%!vRA^v
zE3i}Pd3ilG4vmV-OXvK=eLD-kCmYm-@Ma)L*JO0l1T@ga+droDtD%-)5=|_-9zB&a
z7m@7~v)s_$i9k6gq5n|2yZgsexkH{xjcl3;Tb@7Z!5K2Q!=3;I^<lJ;cbK)>%7;#(
z1tvmd&&z1GTwfs5SIL|`IhPS#7Cx4YJmRm!{@nNVeU!(-#NJ92;aVRf*wdQq+4DR3
z1LYLFJtqT_j++EZAV0lid)OD`IUd=Xo%{xoCnXVOO4Itr`~KkV&&R5HBu>d%rLTf5
zOusihoe%%4^oK4S&q~-vjcLL*HQ30dxknwBo8+T(V1EYlWi9#)-k@{dtGsXKGQdpI
zO}-D*ov-U!HLM3_mb917&;LYJ?C$@a;EGq2Omb)xS`aA<dYPcVjwx!=Vs%uueU7lg
zgt4Ovcp=ZoXn10{sULkot8M!(##9Z{I>x+iI08jXVgZisKSQvYdY7X`Op2b&;QZ<{
z7fFytPth|m2~V6QN97@!p$cN=&fkQ%l&QM$MqpzpW1;=XyTuj1bnOYsn^kav*NWw2
znt)56ZjQ;UF-3xtZS5<TLLJ;{ahNdM2Q&0zh1Vs8WSzc;z`gLR*^y2FpFvqIG?TaY
z=A&>#4B?<5L^l8Y8o(p?@P9rIim3rMCEZ!d8j)07bY4Pkulx@k1^`7sQu&M`Fb;?R
z>|y8^pj%1>w>1(z+AFL($BbkqR=|nYv_qtwAQpMwKw9&czd9@K3${Etp)!f-5|{Zr
zXwEg|#)obbdPx|{y+`m+6K~yL^spP>l9nQ$aI3oy4If`Y(>vUH)a<>bou1kw^~M%*
z!8~$7PRWM$S<FbZNYt&4Ih>^T1WK(udGCs(Q&D90Pr#v_4e1s*Rb%9aUqJObnuW$c
z#}E;Ge29W(efhgory?oCHxsVrIHu4g`YH)WS*>`(I{5nnUS1oKfm7gTJW};%LY;A>
zgO;U|9VUL=MXh*|5m}ni4vnSyrp1pnx!yhT#z0n)nGzkNbU4~DChLr=B?}Va>Edzo
zVeYU`%ze?_0j6&U|J?W{AWJ(jDldH<l8V%$(O*g<oHjl`33XEEuigg4GZJ_X3aC9z
zTa8_LNYcYZ&J?W7VD<Q!P9s>djbHOB#k2jcihJ<#e32%tb!XTCd^Kp_0_b2@nQA}A
zE<>RmbzRm%PetLYVKJ&NlnOMX-J`6VxS())R9BmWD>?n2)6aM}QH~2;qCc$LoTL;6
zc@aG%WTGZ~Uj9*Q_4rTp*&VkTX3yIP1%YVv(JGkMnD5TE@ct4BiTT%W<O`(3a1=vN
zAJJ3#X^`8z!WD|<W|-rac;I6>PR1j!{$9=L9Mpi}c@We}qa_t|LPi|M^_fx9UJu0)
z@|*`yb+Sa(-GpL!ZlB|0dku2$N_DOXffouE7Q^ySttG!GABb_OMhp3;KL6s&qZdeO
zrko3(2p(2PD8cxE>i(6s9gao`xTr4cX;#}e{H}pbpWH?HWcfE~?_pBJlXZ_AUrp|R
z^>Ou#H}jmGe$4jfI~A|b+3qM11Sw{~8!1gL@*Z30C-p?WLZaGAPqYlk5s<dtovzFU
zUDQplJsF&Dyc!sV79km5AXgwPOa_=)u!ZO~kLAEgpRYnNRMQ_w>anBXGx*gj)E2H1
zRVpe22+mLKtB3fn{`rA0x?G@Cgn=F~@eFWloHU^ovTZxEEE8}<rvOr@1n;|vqEYb5
zKv>w>aq?j_pmfR@MUMWzMiZi^sR2FW@YcJloly`A5?q~-$(?J^kF&Opnfcvez+LXq
z*QYq{Bks_c4Kz8SuNw{J*Q29a9@g;xVlr}bfnO?&MxKTVrMSz;d#fN$Q@D2TONJeo
zVA}*8+b8S>^<08Hlhn5#j3WDpK0Eg51Jc=#D*XP~J5Tr3yuNGMReLcbvC9b<R|#H?
zm$x*xznLkQo#xi<%W5mFN7Yz2m%olz7%fR*Uf(?m>N4VSb~7ZvR63xyG@;A<I(cUL
zNm9JiF2;z~{8xYgcdO>0|7L+z48ay4RG^?goj4Wb!tE&Fp?y`%TXG0$@|}DygE!Sz
zP+Ad~Sr?51yeeS|!fhE6)ZaXzpW0IJiBxw`{`V_{VH>)4{t0WfuCa%PLt2{BM$_*z
zdDkIfeA`4^SaYhsXFq+)W*?F58`I=KC1)5f6DxdXq{bA*;$*B#z)w7^1kwYOiXA3(
zY{nD5Tusy_bwhQw23>!g25CJ$Pj4Ab(jAPMHyPu(Vv!)H7n2->ZRPIn+C^uJozL;`
z0&CNdf7|_?kJFFJVo%^PbU$$(Oy$l62vS~941e8f-$ZM<DDF&R)~pFn@bEw+pLv3$
zH6YQ|V1pt@yN`Jy7u4nwZa@%2AJ?Kca4es#Cf(JPLv`0o?OXV3@(a`U|2-M1xr(;n
zzsLoi6rPDZxmY!DInsa(ivx;DWx3_(8Mv+S8f^XF1vjcWt%>{r00O$Bs+?EBdO-Y5
znPO;K<;&+BzEj({9Ie0A8EBG2-IK$sq`j2)8mG!M>kD6`ew_pA;pZvqIWNmR+o^A#
zDQ%dD)3cB+2o8|aWN)2_CKHB@(E^rVF0dyF0k=^7-?G(J&>I@<VX|BReLy4u7EhT6
zyivuLPu%~2{!=kINmyy!Z%vvK4KAAG_}UPWUX2RPK}|KaXN_Pn%M@mLm#~o70f(LK
zCMXM^4s#_Cg0AU<pUX)7mk9_=K3uB8TXeSp;&&G-f3eLC5k#$|EJfvvOnTr#Dn>zm
z7GWb0-KD_+(1Qs`Sy~{+Z*=kQOn|*RS_RkjS=NWMKS+z9G}30juDh-eTIQDj*(u6a
z1Jt1lJO7E^FLCGx<du((j(FKY<VLR=3<lfGa2*~KG74ifAkv=U{_(1X@JIH!SyE(`
zu;Pfj`)f>(NG~JbLs|04oPRIFi7g9}sxlr5kiQHz!GHrUpY^CDDc;G6$&2{xY|0E%
zEjTN<YU{+{t_LK(K(rr6LkSgR&_*(6*<&5hK*%9UBT%5U8*+R%#|xA;!E6s!%jJ4{
zE`U>`TG4Z)28};CeQOHxc(LeUU%=_r-};ZF@E-UI=Ym>&2odq*(+f8D*#SMGVjD;P
zSs}~Q@$qa&EO(KEA64E;BObj7WuMGleGj(5iBWrW#m$Vmo7T4bnOm(OJe&**Fw?2P
z7_tZ`wF*;s<{{juw3uN1i2Wn)343-u0UoV^59^S*V$B=X(e=j_6pOyc(J>}=pkx&O
zQVIU}Pzt{b)7mILe)1%YMgRMjvk73q(#ho+7L*Y?By;Y@%LRhEK<)q>*{P80V?wED
z+7q^oW1vdaxdXntT#);&G7t)mY{p^_AUj95bNe#r5)De5WQAJIy99y3*`jX*Du}gu
z#Yf~V8B{*!uhFa*z~6GUu@~pGXefX{<d;51+M8mJ*7(hi{}m}_tKVICpJP~fHwVSc
zBVy1~gVa%@h&Z1j9TwXzIBUy+=oF(y%iu4tqGUp_CRC>IEwr9SV|b?_50l!weZ2&$
z$j|7yz4<SheKP*uRm-_q;54ek(DCC6yaqr1M1$qOnOZCyGQm+q`{U3tm5eyGY%ws+
z-JzaG)0%xPSu`*7PrB(bLh{Yl6IRU0O2-gsASL61JGB>{iRnz&I!0%;ok(GvIWGC~
z#pfkO=`Li4tdO=oqpE#wDo4g6GiE8kJ#)V-mYJBa{jt3rif&sF><>7mkOA>H`%y6-
z){?aQuan1+v97+K0S{3oRJ{Nq{qD0zwTh~Og2I0ep?_fdN!T&UotF6FE$}%KWvDlG
z=xYJ!GT(@S+nDUTx@?54KOIY1bqLBMf|vXRF!L_YpX~sD4od~l=2vom)OinhldY{+
zL^cU<^bJDF`F~TQ9`yQlH3uEDi2iuUqD4hB*q^7)b<w1_6X#y8q;*-O*yIF$?x(!l
zphv5OU+20pEF2_7+YyniGKp`Y2;2SsfbP-<ct$5E^1y)ww8PS$FEaHeVp?KhrQ~{Y
zA++?V?NaH?p8w3C5KV%7#8|gqZE~1z4E?H_O+m&7pFv^J1dflI;X|`l+fKFCc%!k!
zHIj{zxQiZN)Q#k{P&qi&=Ava}?~KZR!?4&R!5?RVI|HVfD@K*YW-+#ZE;IByZV{%K
zu*a#1qYwJ}`!T>cqVv1bcUrhc+y`3ct$3t^xPpHJP~4=UcY8xVgX@8ji9!%9Y<SFX
zSZdr|Cl;r*3<2d?$giZRZqyNDcqb1{?8hwAiPxJGR29&Kc)`7jE}Y|2;bZW0W%A3a
z+6eayWrG*<rphaPwaU6*s&zOIX8m3;zqPs^`;3Ea#PrqVF;|H1W^VX4T@TQe7n(_k
z6>yCL4<ccvQVUjYUDGGcM+jDGVJ-*2lOF}UE%6l~Nj_2-3f{uT3i+aSvcG7oB74YX
zLC25^t9t!33wMaVDE<jjKp&ujc^$T=JbM|#p~d~~yTeM$qXOys7}0QcJ=DDBM|imx
zT1*~V@8|ONwO_U7+R_y57*VY;v#})s_pG6TEGHN}D-%SOf%5Qx@J>2L5VaGJ+^<}K
z0uy2T27$qtL4`C`=l6W=)Xq9Wb^t(AgAI7)vJ8MSvdl6mspl$)s>#uI<KhMO1Isl;
zG8nj|3MX@k5Txf7?2@NbR8q18LeCMXJNiIRfdRDmt*N--GfKTN<ZHlD{OM;Dk*!d}
z+o8{r8c|z(8N=Rn(eZP5c@}m~p6p12s4x_Wmj_;&;gOnjEYvy%D`NGOv!0OYF>(qK
z$?bZdq(!u{`|klp<5M=oioL_*+6?_kHXW^2+ZJh~Em3Zm@2B#nlA$#|s8g~E04xUN
z&M(Mtp@ftaA~Fn_b=h1`;CfyK@qS6jJqoxGYP<U;!Yboy9Om^%>v_e2&yUAG0`y(b
zHOw_Za|p}+!7zwj0*5qP?UUCPM&$AWS-{*p0P;o|fOX5)Dc`K99qBfLfc6fughOhl
z#rk#Muz{`toX+Cq?eLi)&{3xW2Ey3i#_^-h@4>z^08GMcz|mXj3qpQeCe-En|8u=#
z=e?H}*<DX}{DdohAt~~~mgiU4X`FCtvV?u4ZI4a>#Yl2T3A3Q#(l;rMC7tO*>#eB|
zA=r=?HJgPvi4^W=A%vfx^l#_wJvqgyAN3%L@a7Am-r+Kl;#-Uw#?SQeLijC0-1a?c
ztc$b)F|xFKw9-U#OWmHLi@Y~@pcw=diq~Sf8Nkf6O^*n+BGCC5=FqVAf?WFT(GD;T
z+Gc6Mlg#=P=yUO32}-Fv2CHfJZ&4+4hu33{kTl5_P<<BJNV-c3^+YbG62l>ymy-N%
zb5f;{!&P0Qz_0FZJZPchY8Nl4oF%HuxXj~mP}xht>Pp51P9AZZ7L(mLDK&$)e?Wk*
z`=MM{(ZBDX@V|a8OIB|pI>lBU{3<l`<Gn+W1O-21ByTLjeeGyQVSFcRLnXa)17=Er
zO8EJ8xrC~jA%1y8|LVi|h3}pEs9)8%Q{@Xlh{n&4hLrJPv$7SEWT2ctp4qn%3Ce7p
zt_0s8IJA={6wmRcW@~KlK{Ay(7z9Y-%KeOrr)wVWVr5Qx#<m_#^g>ug&4^}cK@`RK
zn2CakM)|1F8F9t5HX`sGg&-0vGn$Fk4?Zd$qLeP!mS*YpKQP2jMrFJ+yok$A9Em?Z
zh)NhgR9aBUz*vOQG3S{)XsJO?4Djb5o2c3>n_D8=MR^kff(ux%7&1tPJ#ld_s2YlK
z;*keWK{LB|$k60GbhhsB-fT}Vu!JX0xfHuV0NUv^urW!m^u|t&MS?z~f*hZkBynZC
z${y%vkT-$>3xsz|bhl+^2u5zE$Aa#!^Dt6}(VJBtn&ORwcX&Ls^d=|K$*qtyp69W`
zH!GMqNgd3PcNN<#3xDN_?Z&_#WQ5f2ky`t|cp#gorU42_y`rxDJa9E~Zf$J|3;pK9
zbbG$aKWj((t59|7qr!A&)d-J4D2Kj%Z88E(JkSA&<23|7<$LYkXm0R~vFki2#!gZ&
zehl9Jy#uJ?=Q>h-suH(!KwdPG3;O`lmK{p*T|I!^1*Dbaw#tCy(6x%TJB>e}p5ioY
zbQ}Aa{;gAecDuoK;~V432hcb#+y4Q)3OhdL)dS-A^M=8)*+Wgf;3WexdQ)Gs*D)O<
zqnv;Ru(=i>zNcsKSj8BvV4{A|u>hwYm9(<|5}cPSz+i~?JcZA0?h*r;`}RaUA?xc1
z%?m9U$upvS3>#!G4rz63MVX>@#bIG)W{$tP@l}yy;M)VU5hQKXjfpR&X%l~Rr=?Vm
z@C^!4MKiw2D5?U31Ty<w)e)|pr=YqV07tO$t^VT_PUCM&%$&4y`4ambO?SXdeD!oy
zU?JsV`8Hk!*cXv)h2(fjdEBW3{HgXS^KM96x0p>za&m$54oNPHxfvI9`I}LT_h*V`
zqOD%{e8$6-U%}1Izg~$H!<#R_v0E_@7JbsX%4{)+-ZwOqjBJGNHEd7}{SuAxm^F)f
z&r!iXe$KGxDR6|cM18X=vykb#c}%(ypVdu2Lig<nrL(d@JZ>scDBKDUjg$61&Y^h)
zJb@Bur<CY(oqRAQCFKW$33_rIllJ*^AA;buH!otl370lRW2k8<J{Sbr!fiy4=BbaJ
zWoHg<4+R!^p{?p!N@NLE5$3IyovRg$cm3hr+#5dNz9f1p;~58UjKJ~bYCf@owoPR@
zU_bLH$aAf=8}Bldw|+U2mG>Li4NYoWJTOCj!N>~<+Iku&)?7c|2a4YK-=F)Fq=4Yt
zNw<ypRDY=jhC2*OXs5LA!>zvRhQbuQ5no1MrF^lec+NOHhw+cm=hR}zP+lDLL&Yl%
ziUi~O!W8va@tim3a(Z46=RP{OR_^<9s+DW{Jq+-41f-g&jxE_P6J4TDju!Et<B2x|
z0xyLVzk+$HSgVA)zP^Ec+BNDWD&zP&Fo!7Gse1WR82`ogBa2np<&dL~qDsCAJ$?1K
zUSYyvCvxk@I3tW%A1M*|M_h*pkI<GYU>7JDiF$1H2r2IP={Ms#uOBmyP`~ng>O9aQ
zPdW}izD%#7&sfFF?xp?pk(bW+0Earym!T{l|MIgH)yiEy{-OL#!Ygoeb$32a`J|q2
zc+qlfm|Arr7J+gk?3MmK4qX8&M*V4AZ;`pDe=GU|s#iYzMxNvddpyhY7&;#1u|a}(
zd-UWovoY={t%mvo#xby_Xsz!sj3+gI^jI~#bnx`xq&3DD2!TM{6Dxkn`y5F)?^sSq
z<a0W=JFohd`YKI$^hH=@$zG|gKg$N#)M>`29|eYKD-QkgY0q<;>C3>pUFg6WJFj0r
zv#dB>#Z^_N=qo+yW3QoszLjoLQwHOBolAN@FV$AYZJka@p=h`pU2{JA;a|(7hpWtI
zmoL2Jj`lznHQnvi>8}G1m&<JtqWcN-e1R>6&-gL_3@?kIL}9XfAL8&9^W)V~>ZQ3Y
zols3TKihs|-kJP3=R(4Z&1W+ytTkmbpl{byPO&puX)X_i%0%n`z~p;32GtVa(e!yT
zFGuD=88*E2*YVtWtLTZC+;l@Du8jSkdATy^xoPPjA-^|y`Ze)@MJkggWQry)950P^
z0t}%(Fi>3=ABI9&62ahaJl@kzIQ+mHa@}XpQM{o|*L62G;3;7tN||O$tj$b2cw=a^
z&N2&y90m^(>UmQx$CLv)G0h4wbeRHjE&~$<Q^TS}5K}tQZdQLsc^Dbhig>dGbZ_6X
zyCMZ|BLoW~7GebRrKMFig6ZL-YxfK8S>~6g9JYqgYOJ&5V_L4#bJ7A{dse>>%?f!l
zy#54w#5h8$Y}ajn&E9!w+GJnXGjCuW^}}9T7qXRG)5u}Bw=WObc>8q<Ow6T}dY(8g
zC;J==Fp$N!h0G5;;c2@QwrIAJg?6r6iVqsCACsn!jKoiQrAfpJ>D*ko{_$Nnp3<g|
zf>)PVh>v)u-CG`;9;BD1o~E1}fzXb*%c`(81sT8F9`Qx@Rqk;RXm9(0_~y7zZq@eg
zY=0NLLf6mt#rK*A{jvId$$Zl8TRn*=B>?8SFT2n8#uMTdu^)H7IvbIM$JiWuA11V(
zBzL=Jz<In_-WIcQ%$BvaGCbg$MN<}%W`36imf3av*%Y2?Uj*ec)sZk|cq`eGWA<-x
z)#ITfau~w5RSK5Yxwv*ruyno{lM*lB>3GM{R=fAzvIw%ZzE|hIF-$#0gKa;_e3QPC
zB{MH)SZ7)IidsPlSBRdDhHOuF3zFNo=T)v3{>v}jzSG@;ma$Y{V5r|fw_9wk5)78X
z{L^`DwmUZwSm*E;QdjTY&gnmRa(0d^vK!uhPEON)f!j@ZXfSenB;M@^fOKEUy>KDU
ziZit5hl8!E5@39?mIY$T?-g`$b~(IZenh_{4a=ZC_Da49fv@z%Up8-B&W#jSrW_h@
z(u#uZC!FgjhBV>wDn{@CdSoUGH5Q;=5#@|3t80b}o<p-#7tWyLfj2vr-K>Yb*Pf8A
zNcf5o%-bg|mh#)FSK3DCu}uB&hVXsngsZ9T=tStB{<=SY*G|)QGr~z(nw(mT<c^Bo
z4L(WzbLYA#K1~N%VEUH2a1gy*(%dxx@CmKEbc{aKik`KjRH^Q~IO>f|hMHSukxhR~
zt|?EUm8CDxAk(82dZo$vzA|A6u}*WIu3Z?DUpSd;oKhY~NyM25nu?0q;vZ_{f_=vE
zSHz$E>-e(K{(-IJpKHc#iNzl6<oB`C_QObv-Q%(&XpA$t(_)Fm&CrV>_ucA-Si>$A
zTx|dA4KSlm4&CeEw#ew>sh1$F>H`q_n`n4jhs4Oh^z>pJ9Q4Udi{*Rx?#NjZF2RWH
zN~gp~FNCzp`dd^|<)OgH`T2<{^F4M9fI^=@&o0?H&9mEREAj-cCGIa{!xBIIy^Ob6
z-|y$9eC-`_K$je^ZTx<wI(%ZlhRU61yr76`u~wq0A-&Amv`llVz`WSU@Ad06w)so}
ztM&b(fpdJf#~~D4d#b6vz6U!j(0|cxgb`sKz2@4`RvsPacO&GB_)tn6(C*tYD9}DL
zGFso?V&VCq!~b_-4B<;Ay?V?~cWk;!cDJ6@jI!S=z9lzu01`}U(D#sI4GU5WDd+2C
zoy&`vJJTD&{*K;B$axs5$27=Z*_j3p%_>Rel`?;p%#Bk^Y)YR`0;AxoH}PXTh)Xis
z;g&%@{+g+ofs4~MlYF@OT>8fIioVU{H6!r)cw4U|KRhoCpB)fPxjlk*wu?AZ9-%L-
zP!RUtv|OxZ98FQv-o?jr^vR<y$9mGu*E2$wI|m|I{WEQ;VDu}gs*Vdk`Q){Gczo~v
z3LFV9|3w;fV_SLOQ5mKSB#)A$)LmA@$ba>T4I1Z5__t>qnaDMCzSn!33G%zyT=vh~
z+SZlA&-CRT?tk%dV10}|Ik?8dUA{A3*-P7My<DFBnGm1FO#e+(EiG?iEtKutNcJbk
ze!c7Krk#vVsy5{`mXYR*lB^Y>i#VAA_296h_4K?TTAGavpDe8VmzV216>iz})|4mt
zL{0StI_XED=dksaPJhk6D+CSqcg=%^_@q$E-gd8M^lPw|nk9|k&BFuo68@REQEiO=
zuAFygTe?Q;*+9LDz!uo>(zOsA7(f{JiscGhie^W*c{#{Jzsan(;c>3C?#f*5uq5Jb
zz!Su-Z;qe4EKWcCmic<|r@LWFtWFbl>ROdv?m(~2p4XQ9JddH@pi^%%T;noW*VQu3
zcB=C6L(9@iFAy9PJa}E<Z_KEQ-J<ht#+(rV0$Z^#F$-jZug1B45i)QDe`?(9b4qIp
ziKV|#@o2*_?D1QP)pZ%D!>?UB1DPZ|^`)j09DUhL#05%S3=)-UsehEi2S|Bs*ipVU
z$bJ4{x59#b{?9seNz38<LrBJRqh7w3zssBnQKIuCKWSD3`riH#IXWm0UcK7+mT3E}
z<u$3OmY^1m7Rk*OEVDw(x1g3Ax@d9&?cB=VsmP;#_Vivat<6COnuSU@?<xb0kpZus
zYUm*6IvNvwDrv3FFW3OfLiB-CY@d%yYl^dD(&It0H^+a$CYk`ez&aG(o4L?qf#r~$
z+v9p;+4C~xbyB{=z8lViI!8~f6u5b-dM9_*h#wo&?;|58ErnnkG%dDN9?Yg6o8`lu
zTt$r_=k!K-$%j#9zwR?!SJ&cc*HS^^Mb7+tmyO2#(3^h?)bsxc5?_m^h_1#sXR!Cq
z+Zq`KW*qJ@x{Ly%bklw#tZks;l@C>L6OZ6n<`$>RMwBL7cQQIAG!{Yg&30N|+|#y(
zY-WSt&xcqs5PLuT>FZFsLS0K!gXT8kVnp0MvhSTX6|Q)FA)8*!8!+~)uM&0kPoxh7
zG1AL8G)g(1f|)g2ae)L<<*zX!cb;jd0~V0?2d(49&N90HW^8H(d_|)7AJCiEUZ?-~
zoKh)vHlXEY*<*aPWVK>hA$E?v_s)(3#0%z_wOvr&#|dP6>D|@J=?Im{=rg}3;R<CB
z<y3p|?oxNJ&iQRvvyk}@?7JiMTIYg?R>ZaO(dt}B<0Pg3>~0y#SI2iKlGe*-8ZgD@
z0>;tGf3Q6|nSCPQa#?QN7Vj(Omoeut|Da*3hjeZ6Rez3$j_T=D%5^zLnw4uG%}B9w
z4`;M7NhB|Yjhd`$X84Y^f!VRjOrAAY&`CQAZjn9z8U@an^<F<`s@-wlMd)?{Py}5P
zeU?Ix`VN-HtxDNfw_D!7fA}Pv`eq1bu(8*X$(yv7?KU-PY@o)}8-1zvO0>A5`C>fr
zo7xlqF2d)ny5BWpj-Z7m%_7@4LfaucYxS*+eJJ<j)W8_v>XoHAwXRy>G$||Pd_6IQ
zQ?-qVqBTx@xRG5-TYxm)0NoK)fIoo{QKJb2F+UOsBMRaCCO4ZI`J>D#r5`K<MteHP
zPTkyor1ic>6`ZXp=Xaku0Vm+&$)?fe`1_u()CI4PzRE>XbPhJU)3LUE8z`br*x=_i
z_I8<83{r=m`k^$K((#-G!1zHL<$0ij)*nZk{+V)`Kif_tRs2uMct8EAca&}8qduNp
zkCV*;VIsybZ{iL~NJNtM`_`07ZnO4UxGz@~0&|HiD?Fh;M3cnUZfq{mIMH4l(l*I%
zjxuAUlCg~F6K!dnFty#Ob;{h>sdcw-Fjsc?iO|NzSM0O*T_kki?jzpn>mOEkH7}I4
zzKD#H;kbWMVrEFzjB=Ug!Q`^|s4a=pOV&1jR{R=asbjzlO6xW-7fR9mC4M!I2y^~-
zqu&pZcqS9>BzS-2OEz4w7JE1#FPArn%|c5~s6>Aeb8+|XGobGlH6Pvupf#5{)lQst
zHF9f^J|E`dr?)kbGOB8P<3nhk8>6}Ku$tNYxYJr46!MlA2LQljnNp0-EV?r0ndkab
z9_QjMR<IXSwPDt1d?az3xr-7w=XV-*O@`W1aR3z7I1jYIYD6aA3k$~i`xAaiU%iTm
z;3F*sXHv!f-9Ob9yVGMwH&`PBa}CEqJaCJ!=78d>-5Y6LT{3>s=&U%~mC1Owlv5k0
z!smkpC$3}fkj&ukD)aXNfNTN_tLf`!;fab&euHHT)JvxBG|l*A@~jIxx~II;OdC!f
z*T~2Oz)|AjYcqS?g}hi&J_l)8dVL{!DV5kqy1Y)}LwA>0FSf->qR06F^zuU{JO<ja
zdSJaX&Q^G+oN<xpd-{_Idi8H?dwW9OkL%VV%eZNmjXXM{U<kG#&D^o{AakJjF|9;x
z?w~5z=qXwq+b&^8Iw^hQU(6;QR=x;h8Fm%PUQo~sW=2LueQ|g|T#JrqcwMg74+xv9
zC%;suB70zln&_=dp;+ssU%2b2c0aOysz^O%Mg>X#h*uXT?vlFCOIA96NG_0{$FS)(
zvwe-{+u4;Db25pqdzZ|{rpNA8S}FWdoG}kxGu;1)H>H=Pa9WtSc1<7o<>`+D=>ebM
zHNpv*FU~rj<DRq<!=TG5&#mJpoN)+CC-8BTq?0h$EiV+#VB75ivsIR?eC#%Hc<)I`
zMm!9pajk35I0a=39mr1~^^ldnNiw=J)30?X$fK;s0kWU-8xuM{&raLF<IT=*8!aYr
znueoaB*kyBzTPTw$kNo(z<~K#@Biind3&SPiJ76C_fG3kbq6&@hO~c*2mDD7qlv@R
zU2_b^g9kjYD<^b)&v{iau}T)pUM3qCzR96mUA2%aw^$|m)F(cFmPqQ@DvRVQ8=n3F
zkHX$e-%x{u|2QgFoaWx~SAF2bK=+)$+@2C)s=qqvdL{@bmNNa}*>84QqOPU)&YrQW
zD1<Nf`+2G-H~k#fyBOuEqAztsu73}<iN<8Q;H)6~u+vE%{N8GH-`I>>4XUbL^Ut|^
z8?ObP#wNU!djIyb@BO^TQTIwL0(|I7qU1^Xaq|q*9Q4<YxEI9Di?t6l`57xDF%4xz
zCf7~4_BnoF9HxgQ@x*Tf-OQqK@d~n~7uOTamj$;#KQ0o+uI#z^_}z?)i+cnNp5sQM
zzTcS87^<qCbKGUnaAOC+7CB2IaW(2Wp0FWi{Ag0Xq@W<}qtg`QMws#UREuui<aqVE
zn87>SPs%r2jA_6V&%y#blHnBHi<;%_izl10le&G5lkum|u!|<9c>U{(W$vgl2k_02
zjJn;Mbp)Kqf>7n7nEg}pN^(B<%g1e}^glr+A=h17OWnh=JeK48Y+n1sulu9YQeGqF
zanWjV0{DTCjuKwdZN~0yZ=>A~;B3DO(lh=<71bk90wTBLN}m|&gw($aP@#DeRN6pw
zq`~!gvqjp=(aYx7IloH!{bg2KLz)kd>;7E2f{Sr0W$N4Y3?AU95OZDk8ul7?b-`(_
zZsIzJ-40EtumeDCfs1C;zE!;9mOE<}Gwt@T2J5kh&>EY>*U#S)5|!?MfBtfiU6=pg
zBmB0G(1(7msk<#dT<h#c+Ev(JpY=dO*JU~N=Z;<^1)0_tiz(UEI%(=7@UW%HBxym8
zbjT`$r&YPasN7&?X`9Yg^H2HMf~&dtiIP$&Qzl(1S<5IfN2nf6Z?*xwz`R?c?^$*B
zC;F(3fn8l$Xr0?@JJw0D;dOgEY}TRGX>->6Jrtt;0vw#9VUN~daOz9!*9F<y8Z_Y+
zfQCb8Q>vlp6jRHgoSU<F$#(IcC@JV+EDIQ_F51mW*OMlz^p5!Z^*!hO#PbB#znLFa
zq6KOK4jZ4B0c&X;@zxk-ePgj~T%+9Bu8_uwnf!u4lk!_SyZ!6KQVm|EpR@d@v1D$2
zA}5N+pndl_X^1&FjVIFL_n?`pkCcO`&nJV+>qE@3tax>=DUi;f3(`U;FAMz>%wvRl
zkQbUk*p4!huBbkp^)+2@!W@L)vaX_{uDi48!!9rtJKD*x%iKf~9o}I$KgLOP$QT8V
zFX1kakY`Y_znReu#Xk+O>60vvC~T_}ERGCzKTB<CU)P6M%I4>?QmK4+>qb()R#le&
zh63|m7vKfz)?Bl%Gem){<1Kc-RgM0#*RjP?S(^8ro$zV>n@Jylc}5YaovlmH4Sl$t
z0RpWDE!5R8(Y&peD2Z{SKRVjD?>y6x{CkW1*<5g$2xfoSPj;sA(WDK_=L4*VPEVEx
zqTpMuBHHh?QhPa(d_6;aul5%eo+k>4ggPyHbG|Z!rMr%E_39v|i2c6~7--)kFNMqT
z{m7nJl@4>$mS5yq@s)8AATP6qV($1W3!oO5<jB1Rt{iBUzg8Ge(f1cyyuU+MX`a1e
zfDR%&LgKbi_QXtmVkTpMkvFw_30mO|Do*%ao%C;i`mV*nsdKHqWo;LaFfw<e9XF!S
z{qd9#Q_H};9J@x&ZO*#1PnB{UFQ)yJL2@<$Cn_SoU#_WG8}rd%-r3%4g`W>kCkg;#
zUd0$zSy^;Dd8<*G1PQF0SO#yD1!-`p>EcQ(Kj~8WXV+Lv-;D#(P~SCeDKh?k#t+sK
z^n>JsUY%{pe_!A4wwz!#zWA{(zxMSv4P7luCPnCIdM>ZqZ>nzwpReVHKDcX5QE4wp
zef&!`Lu<H{Q^PNVr#?}ZK-oMstzt2?pzbx|^D6#INt_P!G=zv|Sf5d`b#>c7UcAi+
zom_00GS3f@p**Edgu46Xf{z;7d|C{~(JvJ#X5u&V2bFS^1>kXM#8m=XuGoJU)}H&(
z8?u&6j_<5l8Uj^4GFT>qt8djMp(VC5XkI?4k)Ou?rKK(3KBt2A_$w;b3wifb2+~3J
zHuT^n1LXu{klx#VgX0gs=yRUS<DMenI{CLn!n^KLy$0y}80$tB1J}q^GdGdt+>_>}
zsU-g#r2NOtq3-X3nBSAT)%s!;bn_Tl{ZJb;LYov89+-Lb+k6wFE#=FVVBEeEPD5tO
z>*?NA;KAZDmlqEfs;+)(1-8%AX(!Dl<1>PYbiFe6;Ef-1TB-u50zs9p`IJStgQUQg
z0uKi7Nx%KUupS74S8iAN_b|#3kWXdYGO_zoAzF*?W}m>kGI_N^^UJ$3uovp|T2n2a
z_AKBPiS-=EcS<%D&$p^9XdUS_I=st;lNr2b`N<Py8CdMJ!IL`CVn6$%Os}>%hXDJD
zs?sNpvDP{z<ZU^3gG~A_$oBzko}bcZp!5b(R3ocmKa>NV#alBW7-wrk<0NG1d1Ho)
zr4u+<gCm>Ch))4AtnctIle);j02v%(K2-+efXyxi6D9R$hc<iJE2xWRlV#JcIzpkf
z3B2NjZY(CNk1Rj26J!KER3#{3*2MirqVzuG*>Z%R9F^JuIW=8j!1vY<v;6FEseUyY
zFFgbZ?VX$0K9)7|JLUV+`XT)N-Lr5Fsw+8>pBY{M<W)AN=itMiNw(lhvF1FjZJG`@
zzr<}M79*s@M_HTvZjY<XM<Oz=?~Yf)C_NC!J~Z(C1;t0K@_iTn)8eBFb7iVYpFus&
zFSGbD$id7lbBx<8!o=A3<CBfW4>j=Y8QSd@YII>|`zcGeU<E{wTGyB~edhL+dy>Zg
zb{N`Bw>Ky9=(H32H0;IM<RS_Y=cn84If*j~I?lGE&x6eA!Sb|Y1c4g?IvMDco@xu;
z$10ceVjo;UdzdtFT44bt_m7jNNJ4!2433CL|3P1Cb5`)fwMRQ$Y`Rt5*!>j9jq}-#
zsT>&xJ^1%{S}<hZ7n#KQqMeFp3e<jj0A<B`jYpra*3lGIG4Zf~xKbbqZKMeQtd!qm
z=ZAe2|IK%!&E1n<uRf)@Di+AMOz}*35X{z(VN(VqKWzQ=o4?R42K2;>tK7CUF8`>?
zxSVIs9}O7aX?#(4T|n!%1;6|!8`m~B3;z6!*ji1kOqQW9UKoClWUo~UJdtk9tNDI@
z<R!))Zqml~y%+2E!3FQ-asWlkbV^#fUTKugzLB{!1GUBW7*<rV$6+%0I^Dk8f;oNK
zkUAz}hm>ED?#hE%;))5tR&b6w8`}n=c8rrwIm=Q~jC>W|?@crNOxC&WFW^CXq^&nC
zfBa*-lTGJ>Lf>vbTEb&cdm^w5v$#I9KRQz4EV*vZNs5RN1xB4J=Poqe8tckI4v1^Z
z#~QkOsrk_2J=>=D{ruYD<B5M@r<ezVw7-T|?a*k6kYjNa4SlcchQRi0QZ<@pRi1QN
z{dQc1oth(uF~2?)`6kL940Gr;zlk*<<E~KluNe1=iuWT5`B0EQm@c97MKRZN{g3-m
zU5C}GP=ST$vBAq*Lq`3@1*WF3g#<(Wg`~{%#R>m^*B|}QcjstEDtz=>?{6oM24?r;
za2%WI_3QSN*!4a7Mt}dSa-V(b1)|C5Nj}QIH1t2%dU$#+<mK%o`2}b`QIkBTTFBtw
zK4E=VsF0*nmbMuPgU`-Q*Ara(lWE1^?j(m*T?CQ@rPtpc(gcC~u@<PcUHbYy8yn{r
zxSx-27R=7}1?DE6OS@n-w>jF)6@^xUbyqmA5O7khE%M!Z>5-6i|8uW<1kc(nQ5GX*
za}jJ<;-0LySjc!-WP?q})3kVymE!em1Y0WKm;2##n>~I1f6m8N{ZS)0eI?q)pZM-i
zUDLR$wG`I}Qy|q!1)gVQ>i2yK1>t>ZtRwNq!)=lrS0*iY*MYbObXJ+}KQdglZCq@+
zB>L-GPwihFaXbTWqkUNV47vB?uBP2kg@pkyV0OypKR2Pb@C8iAu_GzPMvQdn%uJ{#
zo%msiZ;KTp(RhC?=z<T|CFbD$Uu-E4|F_oC@=wyl4-C69LIL)pdC<mh$0VS}b4i+=
z>O|s%=ug38v4YCJyB42l6%664NNZZE6J392&UuIFPBZ6iQUe1vLPEl$*!vSa02Gz=
zZXrKrD<`JjfA)o~nz=@9_Q!r)c;_wB7Sjg?U{+QAuM*3Q+l-83R|UQcr-?Xb0hp0k
z8ji!e`&RdKY~1}JknHbCY4TjVY#|rPD?%U;gQ>fy>{1;MP-Sd=OP0q_8L9V(9p0Tj
zrKSrH$caI%qk?aJ%$D5($O4U^+cI6Awft=su@Rb^QQDbDCSUNIqO|g$yu5T3EK(T9
zoC#}KUcF-XM`~|Cf-v22QjXXChc=Kjb{*Ob=9J#DB#eOiST5`wdtd>3Zo=#_E(pON
ze2q|u0ekX|>7ZOt8WoIV+$*~<5u63s1ZuC(Vi8;hS!qjK39J$v)DWI`Cm~?X;0|gB
z1k~C(t~<P|q;XO|#P-@R-j82dAmkd-WEf>&3Vmox(tI{!d(>x4@j9~SMOs?-Y(@bU
zxh6$i;_GJp^$|*aN$i$-y8Ftxi9Iz%j!#7LawiCMf!a9T!&YF$mRUHA@z@MKarp=?
zV|0_^{e`JF;eJDj=?a~rbWh&SIh=;xpeWYAu_EH~)sWUc3AKfH1o&t%kNM*ychGJ>
zJ|WtluEc*HGsw)E2!^0v`b)^2t4elE*BP&mq%+bhnsj&C6pKnpbp$+m)PYG$iOwnV
za>0Gq*=3Ns*D1jx@If^dIN-mWl=9?Pl6xzIxV(7EOi$=8m=a(XsE(ETvSS-928Sys
z#g-r`D3Efu!r$wqZ5Q&Zz;p3^e^G#jw##Q+=j+f3?RJ;slQarIVVE>-9`U9K(@C-j
za}K>c#@^>mb$wDdi4SAOnEl~6_2O|jn?8f<deU}t!K2RKXNEp&tD3LkKYiltJ8$*K
z1hwNFoc<Eqf*l;q_TY09%LW%}1N3!0#O3YF3DY?SU34rua=XYKI3$<nHFD4D573o%
zK13el=+HYvu=MSH3!Y|+$o+56qL@b^UGQc7ZTc5W@lA%NUI`JZKB$+fEVj(%W&VXW
zxW4W0TKew$ue5a4_zpxtg0!ntZo2Hiv<16~v~gYUFCM~BQmrWaB4fqep9V2@=#jhz
zBW7K$U_g3ILFI{SAb4(nXLgDirw}pmAF3=zzI$I_ld7^1)2Rl!Jbd`yp+@E4&dgc(
ziMiu0#jB>o2U}ytn4B8h?N3fnYFM|ui47sdo*F;w=hqsnUKIb8!o=@3{TQIIOLrfb
zKJ)LLkNJpEi=+$4&{v=OKjz*tDz2#M5)SU}?(QzZ0>Rzgg1ZHGm*DOaBtUQt7Tn$4
zEm&{}{@qTVXV$#4*0<*G{0j}~+qdsIb*gsl+8dcUtMFtgh%vA20^m)YAi?u2GzCbj
z34u;+e1m7l`Y3Z;=E<4k#oBF)LRl$nwDXeGC{D5#Xv~me7^0&02qQOoh_}Sw9>&>K
zp5AhTBS)vE%pX>h6s^t3RA*7vB@)_Uu90h)0%%G<ug^`;CgL>NCJD+|PHONpYR&Ed
zI&Jft`ag2|e;EjYl0^Kq#APZS`=GjnqjwUmf}$aN_^2}GrO^Dd|7x7ouoh$`dPMhA
zU{8lGb$Z{|yq$Np5)0+^_jAmS%mQUqS%pt?g`Q`V9zfwohlLzVm!@7Jo2Snk`d-T=
zorh=Ew;UUJ-qm0=nQMH8NxEJh5qbyg)#WBqqT~}`D(hSa(9BrVYNp1aar*WZ$nGp7
z{}@ZE^%H13g`m1Okjv|5#%G(P9#Nqo%g@ZCUu^6)<_Y^(b)I*kml$mn6@BJ=j-Y6u
zp{=Gs^G*r6oNy<mrpdh_=DW{!F<Y)PGaoaZp^*<{wu&gd63H++!78i#9Z&mpG^^OZ
zLDl-R4m+L*TL2Kednt6xWyNUbsV}sKiGe_oJMtOg0Rw35OMP7Qrp40w!<Q%c6${uu
za2%FPGHPNxif0!5=wOve@jl{<4mS(qLq<Uv4+>a^fA%;<o$*F*es!r79&$6e7~TFB
zJKg+g625VdM5UIJGM)h9GHw79ttvV?$J*Vr3>jqGj-Ah^2YEy4m72pqr^$Xmj(pmc
zU1^@wb^m~^^eB1y)qMIx6I!&TWFtNOlayovFhs%iu>dngi&s)$2J8!)HBY0<q3K?U
z+i%-jB9+bhBb{4(8K{F-`;`XiYN5E@UgY<3S;s6|tW8z2KUI_x*$pll6$J`tJRLR!
zvr9(n*_5PVhc%TtX^u<W43S%)C@D9)q`)@THQkHOOR(*&3UV0(h6hUHHu<T30aOiu
zG`)w`@-$(;++_FNf`zYUVXe+g%1-yjbcyaLf!UBbWg8m~Q(ab0)3Knv#ospBq(L+2
zxO2h0luY@kv??X=E&|Ph1dNQ1?YL^_q<jMeS=iNj!6><xcugQ<76MvTqX*+zAnS&y
z96**fW*VM$!OCCL1#{34!M?7l^VRF28^w<lq<RF>Ua7NR=l29u#l|pO;Ph5g#^_QK
zcgQQ3zh0S(7N{Ysr026SGUrlJm6MI#5Rrw*p?Fv@@ic~yGt6#&|17~8HUAX%J1}NN
zzB0}?UdAco+g0#HIM1esD$8D4ELWEt<Fgez*X9=fk(${u0ivvI7k;2rv8_mfWl~gx
z6+hNlLRC$%Ev@x274GjuVr$&ofVKtnMka=wN}O?73rGl=+otV~I{!SjC{}<s4PS`0
zU`RU!5LUA_7@b|2zC>>QaT5lH#jZkGd_MfVDP|c4rXv_hp0{BB`p*KaD?_2D<;f91
z!NM1$FF7_h+%;vC#L1=2V$lOxMvWDILCrBIR4_7k^)%O_(QtWH@>^bN59rRM0V5>J
z!lOiQFPmQ7sm*RC1s#Y4Wpa}gl%oJ^`+W2D<;nE?q6SX}b{Om86~Ho;5ljf&&)D9>
zwfon{6Mvoh8j9d(1o;>^0+^x;`M!YMuYJ7C4lg3I4b-U&|IDPB0plO4Jj1%mvsN*3
zMvz}s=m>tBEBMNT;n|+y$mX%aN>_Smh(VXDOZG81O16vHIh^7xTu08htLm0x&hAKa
zN%oNYd2F)k6kNBNgfr^>Py*>NzBI=>sj;FPJ}dejt6l+)1O}@LuV)Vae>G^|Zo|BC
zj$_g|W`sx^O;&=U@Rc%uMxBIIRVRlJ)WWGJgfUyGWqy?RRT5oK1)rgy7jKs@LSViA
z0cck50a7}xcl==eu)XCZN;1%#+kiZSl~u2ksTVPsHu466V0JY1JCZ(5NDR@To8a@q
z#9~=jix1jtC9UP|yfF^gDu4j$u|S@of4U)&>&<lCRrdYpPCtTRbxpKDN^0^*d&_mh
z6u0-CEybtuGHt3eb%?r&f+UP<))jj4ul*UkK{MY+giDH|sp(1kkUTVk(uzYbAd8PV
zMkW~mkmGsbEDSI_qPfCLKQT6d!bCV}?jKoAH(`?<PVW=%t_e#5DYAf+jXoAC3+_Sm
z?*JU!<c$r3vg+zZD;11nuj^0-2m2JGQg6AZxGWK@gzk$aCDVK=#_jG1wj#@1jI$$j
zt5ObXt(JEjr8ZS95}+}f2S8SX85mD#zMYx_anWSm-J2m(J7tSiQq&NX9+MlSza?TM
zezBYv?FcUvuQ&<WL-%lK-1NWA32I3K@RXB71u86DinDIfETPU<8N}IZF=Q>DfU%6-
zbw~{AasS6zl6`_jKYbltT{B0=spyQ4kC|%Pl(8Bj3T4Urk)jbY1h}>-uOG(?+7<B2
zMQ6-2^rsr_bVRV%J1pX&Pi6g?QP<{(&*}kiPX9-mFTgfNg8B*cQvprNj?Deh9S=Wt
zh3+ndH=vO)3JjrChdR?@fQloZ>#WUW2cKO6ui|b%v~#4*rm`IVQYbwm|LCMD0KW_+
ztf-BJKFkm84ZIKrGRaEl<w$W9#1?mTb-n5(c~t^&pZ_Wb0UeY=jVPhV={P`wC;KaA
zF;nZ#*>HXjx(e$rccl)BITqMqKJ#(T(|PybMAJD??=}TlNbyuA7_w|2|Ae9s)iezN
zqkwxqH#Zp^I;@sTy`gN#wd972)-x4)kHJT@ZBJ4kovN$~XQr{y;pSIn%H(xzl4V`|
ztJRtyqgl`HQmgEej5YZSaFfS!nO4KY&CdP&*XKUvvK?T*4-~Mpps@~44DP?!GQft1
z)*Jm4i-GwCl&veSP)|gwvewjX9nXJv?M_Kc8@<{e-3OZK^|^6QfIX#*xOk|~iSFhM
zx5XXKMvCK?3hwz^OUbN|pj!a76QNHoanKes{vEo1P-(8-hS<PmeO-zWOPIg|c=7S5
zq=&?k(d``pWY?s?K(I62KRBI#LUY{%YmSePmyRPWlab#)JJMw+GhSOv0|}nOug6Mm
zxf=z7>zqvG0E`vbVeF_ehq**Ps+A;#&!gO)POHcn`}?cKGm3yE(o3CVH#i+r29s{H
z-Mru|YHLS;ITSb%_bA4s8tOR_e3m?4L}f4-RZs4~+NUO!0aGW6>;vVpfb6i_k*N$M
zFcN!ffNeUQodIMmIo~F_+X9Anuvx8I0WRj=cf*(Rc3@P?ywsQ&@mz5!U#jaFjzF$}
zRP*O`&iPyvl-$jglXCm8k)92N3wiqut<vbkC7?Q!dhUDfeN~_LB4^`(dWs@=qx7&x
zBTTpLsFkJkA-|cU!CT6%#v38(GsC#ALIUK^Rz0urG<!>rwCFLyS^97{mV|*kA4zCy
zq_fVu<?|tbP9`?;(UZNe8OGYLjS||6s|+APBEp}~jt|PeBwM=yw}ko|O9Xld2}V{r
zqC&<<3~wbXV$Wa3g4)0A1c%>qh=XAz0NKa=+9^Ro2L^!6`A4mTql+S5O#EJHR8@@d
z^?X8pSX~vnTBy=@wp=DuEW~BwX*^y%uFhH^w?pXko~%ixr82pzSl0P<*ApxFwC8*N
zUiRnnynoV!l9Vn({4->sPH8Vo$4z4DvN=7}wrg>851-<ry)|1bSuG!$V?s1vi}1s+
zVgq@t+nJ6XSn$)<`4^U-r95-8KT`TX+R0*vC8|W^=2Q3lFhoFlw>J0n;;aYiPWX8n
z<*yt($f5<9Y2&*B$>V}#yg^>d+`>Wbbm?k>AcLdPpN2AL{=WKQvf=yfvEy}DB`Mar
zEa&r4gYkM-i{3@2N%uqOCx2pPKWj}^jn`@0F{rQMsHYc{GA4KnwWus~`bM@l9dYs9
zj;WeWV|_8bRn)NAKR6VAYXMCgtspaNPeLara7^&49*k#}dAqUa?Bz5~iAx~|KE_Oc
zSq4F;6M@J<{V<FR*DU7c?w+d~#(Za6#dW2ho6r}cOq<|Y=yD6*CI$?j!Zr+UeLvoJ
z>1J`$4OhrT0j|5yP3X@|TyNC2#5+ZnO^kX*=_%Px1KL-yV#ssg1V5KqL%R*1%tN=&
z+h;!E;jb_%3Oy8sJUyA6q%sZkCbmNsLgKOoG{RF`k$gR#eqIKa@_PA1Xv5vW0M8y!
zIxMoE0)v_)CzKL{oS1;6NhmnjcOqNd0+IEQI<YT65~KqDag8M`D2#6o&mx;F?@u4i
zn3BcT#2p|lHI31ANzwfgZUhb=YN_(&NuDHWs@FfBiXm+@6QOGHP|tHR<?;L$kJ!KJ
z(B)Bb@G{Db;d8oXezC#pk)rG-QS+##!Xpgd)0CL$qL70DBX;SC@BO3ZUZ!Mj@`*-u
zs(N`e?63rDPlUV2=0Vyx>bY%-=L*zb_JiymN2PEgL@F`)lfX|3Gnd2B!k7CIe}gBy
zmV*<+H2=;!opo+h(Guv-$RbLQOM^nxz%W#JyZ5E&!y-oPykN`It>wpr6_%O1Q`WSg
zt|r6i{xMX|^5ke9x3g51aFVoH1z~Y}%eUOq;8`3hs<CYCigER+ECi0!=@)vXoCxoo
zEW{+ML0+oT?RL*E*wSx#Jy*dbn~WqcT7}u}-48NT7|VrKan}+zrQi5bzLj4`&2xp1
z+f3we=-RivAU|}wZqD<C)_4GHbnETO%s4P*=)ol;gJ6LV@qLXKi?W3L8f2YXPCuw$
zKlS>Oes1mG_F+KL$n4_{c-rNMt-lK5{PTsc7&gx`J4NwkZI@S@Fg8K4bNAg<iWTAU
zB_NB;&<&V+p%p8+WqU@c(e`^bM_q$!ydon>?8V|3CLx>%k-EFRX>9f)%jIt2!fNpM
z`45ztr^BJBma2_F@dA0|zNGMDLT=V2kn8C90cW6PwY;XjYl_dAjbYNgrU9AY=F#*B
zR5F;Y5+dV1Y0~an=~LX2Q|lI#Sg2?(zqdrK^<XrPklk2VP`nRBjG&NCp$spsItdCB
z6{@H+G;AH~$vfQcuFD#)>Pn=M(A&k8ft`44nebNRyOe-Vi^&=d)UfdTW8YB){qX*s
zIm2+?U5`@%AHKqbh-W}QdUycI;n&>&zlDc~;@a&g!30SyT)1Un&9)D)6F!JE##XX^
z<9(K2%kEJQTRI(#bh-n6)?0kE>DL8t$Vy)uS)PfbYTazrbk*$HOVhsP-mR~08_y4Q
z`KBeQ%W7bmT%=qa=9t3>y3M1p`fYnd6ewvn)g2lv%}VO(#ghH+4or-|c`^tGVvPR4
z(Zhy3+JYg?YwGImH@!WhJgi8kI6sofCUc*T56lS#1wPOkzFwt`<BHaOiHtTvJY(Sq
zi@+ga%(Hqy;WIj7@3^YM;SKsQbQ45ucJu6ad^Kt|UfDciC5w_gup66@l*T+wfwgoY
zJaeQdEP*%X0(QF2bGAW&v+moOt?Baf+2K5g+}jN|C`TbK7v{Q}h{dV+dVli%W_a24
zO;PBg+pDDj5Iwk<5ofQ+#L4NCvoGjP{u9t?R{zn@TeF(e@06m>{Tw(E3c?!+d=ooG
z&;Y-klw;kd+tAKYlnwWC!g|Nt)4N|_m`Oci5GQ+XRLz2H7Y&fHd=lX%KUo)?f(uDK
z<SNU`5&$c1CH3h(d@1UYBAK`4BE5H<rMh8FLlG)IQ#OTdR&Zpy-J1Fi2W(`N7U_Jm
z>2pynVcrsVqY%~h@q$lx!v<8@#zo*yUUQ;Ey=HBV+aC8rLV`icDmvtK?rG7#H<+Gk
z&z3RX7Jqt1VBn<!I^|}0tqx@>L9Bs3i@$yAycg~5sEZc`YV5r4N;}Qh763eHZdM^}
zO3{ycecgSeZ6oxyLh2`YbKM0cl!YJ+lRo|G-Rb<TYxY$pq}=&N=`qUMoW0seNIyta
zA2gK4$;CAcOksV)i7#<x<1<ZzW*zR*xQw8Pi$^bBp2*=+9ahMYR$MOOMccu#v{dfg
zUU879btk!H%TK2L6N}33J4VaB(HWc^<TUdE=9l#PB;B?UP%^H*xT&@QUaeUSZx<+U
zq&N$6!+|i&3mtO?Kow6HCwy0`ci(Ytc>>maiy-wom_P8LF7^G6zD&0sw|i_d-2)R*
zFhLPxmU_hrYt=4fk1@9*b$C!BxRTWyI8UQDfOvyYd+B#bKa5e{1O2qW-5R$t2cnI2
zoMyb=)|!lhBC(mmlejI4fk3yn;N0HYVkkDJZ4a1jOR|85F$KC7<PA7mFUQ3=iy)^7
zYDK}@<fa^&2RQy9vK!r`C9>j<p9uEW_s6ZLIm+sB7Db-Ommu(TtK`ntBA4vid_1uj
z1PoG?Zm!7N!GSvRUqnQLja>h<v#%$8@%@p1d&O;XgSHD#03?B2&_YqL<xCmXR~+nh
znz`J93ZfA=BYqww?8Yz>ekL&iCPK=QE2$|H3)e=ouBr&dknkM*X~T1>0Pb|k4+99=
zxY1R5j9!4?HY_L*-1eb2*6#D0(DFA(Fi|Oc;OLDZ;683ab~xo0;L*;aNk;u9$%;KT
z7>IoJrQ_g}m19O$Y$9vBgW3qh=zhcOCIgP1M*$%qGz@BKZ&8{)HQ8Mq<Qgv*NV2wn
zsc^J(yvSBLqxS6X2{ot9kOA;Vk=`~~F^5*3aM1VO#~K#|WJcF#`Rwvi%z^G6!!yO;
zJUK2=7G3J5I-86|A-}4<6KJ+odD=`<fhoKKnLzC=?O|MEycBmL?}}{Kh2<GXtKhrO
zl2;@^gF{t=`r2YVQ9FKlrgRXuF*s{wU50JB#Uvb0rX(>)mDPMayG)##i5p8^^h8>U
zIDZrE2Ky4!N$hgBAB;WI;Zx$5U>sw3d|&qZ=?u=FE-}HE0>Jpm&)@IL0d1nHL+K3{
z^YM5|5>KtylZCutp3Vm(?KZbcJh3Z0#*NZNkwEhD?=|=5Xk%ruI;@H^7L(37d-Bw2
zz~jz;LC&zZZXB&gT%Vg^&e;=ejr|^@o)e!9L1cwm<Mw+UCZ-!`4Z7Xrzow>)vw=U0
zTD@;`M;W~%dcPMrNSq>{)$9c{{Jv=saNDb`LTh*Wb=a;n41?$t*nJF48I?rW(WhdS
zHwJ0IrlFvC;)OBEqy;)3qthfn*}NiuCIA4vri8ybKE80T&Gn$ammkwCA3Elz3X$Vm
zXlpZ_uzlgFbjY!WV6A#5PO868Nhc$s*KV1h;ul4Q9y0>)P-h=gCvxosz7QKLZ#pNw
z89|!d=Nj`BRj3`%nsMfMMK`!O#g?0-Gb)ti1|=rDmqK3`IBMDSX8r9L|JsmM?`+J{
zng8+4M5ll)DQ-pJCy;X>x7XM34Q{42pU<3g3LJYt8Mw1n)i}O_6XFK?u@*|XovzV&
zZy7-m;abt+wB5ymW{c@b9xsZWfueL2nB=z@09u@`IKJxDXA^z$m26j`IuhTLjtcTI
zqdl(=0Q1~Wp{k%87bx8XrIz+V?rxW0FqX!M_STm|-)657x=9euIxZJ<%h@iJKPo-Y
zt8T7S!4T8ZWY>rS9g=(^84s|dnu`@YiyS&YK)8fEhwMR7TRS5;bt1uxs@}{G8X#>T
z$NfU)s6YhWxC$%l+TH0^{^<&jDA1kfeP{d;ff96Og`j))U5QcoGh)?7sokvN5MErZ
z@jPJ^82%v^x9<+D0(wN=fTbO=te`H$7*h@%iqHkjL(icw4GAE^$vfO8veaak<)-=+
z=B8@%gZB;|@Pp4%rLT|rVn&P|L#e6&5Fac(aF~GIga?Wy5WimfBuTik8?)orAkNQc
zsAO+oB{I;f*qv0rIE8*nXMI?M9n0p^T@bvlcs|!(XXk7fzov93mGllnGK1T6Qo#k_
z!DYQ(`AP#?_870|$G2zhUV!7&M5C(MO^4fm2OY!A3+E)!!SE<H5ywZy2eqLq_(Avl
zM0lX>6s>69-a7onT@@k5Y#{p6*}MfHW@)uQsv<#ZI`g@n6<5O*_11ComtQ@z_uKlJ
zy*0opaMJ;;)|eRpS=ZM-0!Rjwzxb=T7hFZ1kv+6$0CafbBfh9W*F930teKzpMuu_`
z)|Uj|r-nME?69;}qBhCBXpYIm@11^?YoB-J0h=y(j<7urpg)MdEYz+U#sPpm0}4QI
zgjsc)T2&VKwt%{<+&D`SM4^CiB_Q!T+b>?qdH^S*Y^!NMV`THTpM6{?b7=&639oHs
z`mFxBgbkk~v+p+O`F`a?q+A@@<>IXU?it&!9VAJ4b?p4TO#Oi3X8~M+kS=(1ne&)!
zW+FqoVtp=kM*gfp3BZ5X=3e&;!hu^u#ZhYBuB9>oE*)<Lb<QXOe{sKm_Wm~FqlCRR
z&f|HB+ey>e)m;gh>shFZo?g)k0S7abi#gV?N!+Wt*W11Q0|3x5gXZ!<amObvN{z@z
zP>iRMLp>)rIovg82#EH@fI;)TMf)VcKg(<Zm>O4DuvAM?788lf#V(e}Qc|&jz(xp!
zu{LJJ;Au=>i;>=N_<7sJpKit2dejR|WRal4cy~^a@btSwlJ0}I+2u2OV@kP$OBr{T
zPX3)!wB2t#3fqDe7%D0gw;S)28tE&Nvl0!vaMPU<jrk`gZ$zN+ffFVtC|=G+j|&HG
zZ!LWNVBT`lX`f=F$oFBw_mFJGj0wJL=1b*EUnhdv{_E{E%Wzt6+^GMCW3bO1N1k#)
zh$)HipKg>rS;NP~)79rXyhLIc7=`4PFUW~3eoq$8T{G@OKYcfZ4qOhI;=@T!6VCk`
zm4Du^pO2(<USV;Bp|Ypw-9;)6W$HSl0R9FF507*VH<E}w+B@#ZMkHGvsVa~gYmK@a
z{)gxL02erE<7eAkxJw(_mu=_@FX)CPqu&f4CioOyLQ?{;H0wkj&yP~-t%WIZ77Qp!
z9+n1gX^)16A_$%KlvN&n?^3P3Su8gk*)b9RJkcK<VcQ3L04!)|pTgXSAy8iP)}CkM
z2|f1`t^iD91SI<AAbFL>v*95IWSl56d$ogm-_ENONBY0)nsVzh5#ofUbgnKUk7Ras
zQoi^<e989cfum2BqXOFWw(G6xLO0uQG;jCePsP^DDtvC2;CRH#-(-w!-z^(RyNX#2
z7;g;Ji|OtMYVr&LSqcNtJWu_d^5OD`nEaY$O!fkYU?_$6OXR>&E<M2yk{<M3)L5ju
z*WsOuMFoDRp`;Y^uTC1T&Sdrz*?c<Pe-3y$`IwP^<b*piN<l)IX-H>eJDbOh>DEZy
zzbpI5%jSkG;uV9(W8)Ute)mc3^=j*F1h9hIe>wN}w+*B?A$-5^fDzdnW@5V_w3|4n
zt>qN?WK_3{o-xL2yVU&6^-Hf|{CB5!E=X-{ZMQmT=h^QiCxlOxh>)Rr+1Y?y>Mg-5
zqF1}p#1Qy)H{_-LTIKwpbOv=yU{xWUKdxSJ-FTE-8`oswLr5FdKMo?{oCw|C)H_?8
z3)cBOwCz<kZ>GvSbQY@uL)uPP1+oNt;hUWbZdrB``)!KxD1H~89=1__(zLmsPa9t!
zvM3!grvZt%NVChrwFyH=6A|eIt!kky#_ok+|26ppeEq8e=fubtT|M$23m<njG+g&I
zJ0DX((j3my3FmVzY<J~PWdwtJoD?(zKU=jJ?KLhDA6{guhp$)NhCp7AWmQ!Y@a!q2
zB+W)kz(s$#cRuOQ_14<CNk7;6x<slXty)S)x~u{7=hRg8cc;rv#^oQT{SPN{2yng`
zcr5aUOlI?OnCWS2FPA<__4jKJM%2$3tHy|&^3&pY6W&!?0Fq%uIN_`e$cs)<y^WQJ
z-C;gkLJL0yk*yLs)tr9()Fr$MWN+As_h*>!L+0K?+qXoy#t+||V`84QF~_$MG$gaT
z+vIf`<z%k)TWU6S%Giioy|GJDMme=J+DWz2vt`I+<2TWG=AmrD`tIg=pye&xB(Jb8
zrkkwg2NZ#E6IsH-%!pa@3%Ud=yM62*wd(FTlX&$wYR4O{Y~QH0vTg}@`?WU(zww@|
zMs`L{TE~eQoH_kSRVBQ#B-$mg)bEX|_kWMEThUI|8PU9>w4tF*I^jjkFhMVt9H>ip
zcXo?8cs;&J@U)b@L&($Y6+QmJ{5>EDD`Dt$M5PK(sVe;;#wY+##2TiE6Zk}IOuj0*
zK5Fe*ti+PJk^g~3t43syYQ=kpFkSa-qhl2jk3yTU>RSi-AIDrTI9KBNFQvPV%r%FG
z9c%m(UfkKiNMbVzPUT=zlgF6f@R5Bh@X2xp=rOW`$D?`;^+>h|ZHJnzmu~npvaP#r
zJR?+*zQ&3vMqq^_VPK(!+w6D-SYzIDp@sYEP0}GB?s{nuRt59^F&H!qdEZZ_2-|Fc
z;GM00fF056TeD(G?qQI9h#h{-`ZX1H1l7}#pGBSUD%^)UksjGI5CSsV>o0{h`h%Ta
zrLleS$jsx}+@QR}kpHI)t*vH}A05p3?eQ>cDElYCdsMU<#r4+j3Y_>!eW`1>0?XdH
z5PEmQBff#!Y&MGRYfF>1dy(xS(N{2bdOP}<gBrd$VGyn~@P`RCeB3}$Db|B0XU8$x
z9mC%Vsofp>hwz47cHB4<@}YL2-n&~gE40}0je`RHo)@&(R-;DGt|&2H?!8g)vv2B>
zs1}X-v@8vI8id^6708hf3w&zh#>;hwcN~%WkdTm$Ox{O`z3=qAHs>%!6yA-`1Nn$~
zd3o&(3cuEZR5rZ2*+T%~we^{ejjBx+xzvH{wnmh@O~T7k-G%odo2Z>6$7nkhhcI_P
zKAtYhK`w&wU_C&Ph5t1Q0C7NV-bKy6mcXa&%)3W>wglce4sRi4KM%ieymJi6WvYa+
z%GIdhpDk^e9R26i`?m4U&ZX;kL$=pN@cXpS;jSXwz^rB?dxeg{u3|Qktt#Dzh{G*D
zWCLUy<a6Q?RHrVl@KdLyK~IggSd%%ycCjUxJNKa|2;RwZ_Xsh4iFHwJ@F%Xbdx#$g
zfPy(0sN*OEWE()j6cT^wbKka^0_u*`nGT~rEc(A_wg8%CH-oS-o;Eh$f$}+n4vuVj
z0`MFo2Q^tWdPx5W-p;8y16~op$uCGfZK;Ov8|dxijm<6hviLv9KBW0S;4l^SU;^S%
zqE)TO^n3uxc^oq&Uaz}DHvl#9KA-2r#_$PP_)2RolY&>1Z_kshhu=y{B>Ydava%Y%
zKb;p-hM3aSX!--#+8%%ir-2Md0UDQ&=jPMKw@~*V?&5JQ2dL(&PtPkh)x+{U>qXur
zAd@G)qoO20&{#ZH@J@>Zii|QXKj?#0U{LocsQ<};0y62jxw|_q*aO@Xlo;>;sg8!Z
z#l@3pglNmI&)xX1)WBtbF?s;}+6GZD7J=a4;FY326<Q-_=jY48!7-^ium<rV4hg-V
zx0MdV4u01J4@Zjr<yd6t9RMPRLXR7QNsG!mrRxEhwG9Dx^)>`Z>y-2wZ+Zh|!HIzq
zEeR13(agd^iMQ~X+j&U*DHsI?pv4a?EGvvs5z_qk@LS68#Gg;$p(J?f;2OjkG%DiX
zo;u%>So|)(c#-7b5pq#cx^ELAio)Z{@rG=HUDLi#H@XK7HAB&Yc%Ll@lBpzdw&g_B
z#2kU=i(cpLP@E9}2dSj-FmdJy*trCm+XHWf&;n?;-J0r0Tbn-hX@0ge8b|o-0o*{`
zj=yI5Q%F2G#Ky-hE=n|?IUt#{_1uZ1X=uii!|Y$d20k(b1<rSk%0!U%(JU?O!MnnD
z1U&Y0)bSv!B{>a1*z`dSYu6Q9n3ud?O99$AWR%TYu+U_EDS#}*ho4s|)t2=Lw794S
zyon#A?i_oOc!m}`UtQPf0$LDK%P0cc^l0%ekS)MMA*$?Lj7{I2p{zxl&-$Q=M(~sx
zp^7GglR=f>_`jSOn#e%6w*$2rN;<zjz&E)9wyLtXPLBY6x}>&I420a3-{AWVG+hqT
zv;a$BYS!V{Ros$VT<UkXJpd@E0DAY7olooM4emHgp1o)LfP65%Zc!vEyQ@fT*C$U<
zYc&@WDNf0e#ZHsC233a=eVTaNS-IsM>z5v>QJ{%8hAJw>teg%n(I8f?zN@Y_N9k)m
zh3h*L^5YmeGL!59Dz{pP|LS^sh!Tuy7u0Baz6ckd`wpE4C^%hZO!`OwaAIdVozz``
zd%JXIX0&?A?h0RnuRJ~Sw!FDzg&?8L{UfM}FGET+Z_7~^2;#?h*=71aHTx$)AoGgE
zQQzI)j{<6Z<Cp_Q_BYRB+g+j=Er@c3N-UKrg>YmoVu5i+h(oP?aps?M;KvOgGp(si
ze`6#vYhgf~m>Ko_s`h`eIYY+qBR5)<uY<cyU5vBlNRoOAn2WVSptlyGC#SEfmms3u
zFQ}EpcZ_a8(w`~ijLdw@4{2qBDw0%#FJs_A9-g}ftj>mku3}@`GDsp1<o8#(W>V+2
z0}CaGU#b4bg@`x5>p20C>YYa<3)zE*sx*QOPMMj<s5buS-CD$s%NchCX96I$D^MAv
zASf;0_vG!=LU`83(g*N^O&u7JI0H{IMfgw_{+dtwucd8^SvXR~_|cS=Ff}!REeOpL
zPS%5!T&6c7Czx9q|2X8ae=b;hRci~~JQ5@koC^^uz0RuHw-)k|+is;uU-2;5**H)c
z(W7=Z6E|cJI~2et4Ovq31-VsAOSg1&l#Pi80wM#fMDv>=lOWOwZ<ogq=QNPK!P|}k
zS59*xBAs@3lGasCGM$gcTRW46)1(cXLxMWk@j(Roi1^5hZ4`g4r-g9AKOhi4@Ac-}
zJ_X4`#2O%AXb#VA_m9mlujC~egNE2~lo0c;0X=DnLBQ+7R{VUd*_k-$cUONk9p|UU
zfD7W^H;DQJ5<n-uH2GRxK4`x!w*B}GyDSk8K8bjehwLFk*^^_%!{a4Kcw%xuY1tFo
zkp6-xDpr9zJb=;4BJ3NVhLx4g8p_o2NeR&fSDC{l!7k^*dPj>B$W{;8LD%h{zo5dd
zvW_t#4jRN5`=fzZVW-?8B`W`oju&-@1Q>Okbz$>+d)hoPP=^&Ue4I(!1C$+;zE`zI
ziFSWRvrm{ecMxkRlo##xDrV;`Sm~mtMIS&rF!(irCO%F-Pu<8hBVZjBF!&ARpl5Ak
zv#*->m~60y?tk8T!YpVFX4Z@J<7;S8VE--9$#)1lK*8<6B_`&MgC%Pb31q<CS6O65
zefl9|>ex;vvT1Dv2UEOAoo$l@u?j;7sheeTn6k+AOF8J$_>PYxrEbly@XGv1b0p}e
ze7W~0PSK#56<hs$2@N`^u=t}L;Jlef@a;}H1FR8P;so!~pMOaT&j>vL$IAT(AlFF+
z$b+(}CJleGypLaTEA7aaa!<e<r%UY8OdDKDEZEj(XLm{4fDvm;liF4MS@@oZ6<_D+
z)O}AlbBqgP<i8!<0j>Audj3NK<QQP09M~PbdhqE&+k${)ZOq*ipx3i_)ah6rkaDA1
z2>~{#^1}dtZSWEe$Fg`7CkMM=k;o`{XdR)%e8ti<2dFvjT*Hgl6^?Ntt9p~q<L3s(
z5^C@;ieWyjh^DAT*GoFWb108|w3%`n_gF1WzMs2<qee|t5gYpKd^Jon3b^z9t;e5%
z!hGQXA2`l*0(5@*wERv1ktZ5OK0aH#OnJ5J#X5{Q9@S(o)@o~PG^p5}?01xWavU~1
zPpDJeppdQDea}2?EZD|9YTj6>=47a597bOI@%O`?jP_fTt8LMspp?1oa-F!hhZo2@
z2qW)L2r*bl*>+b~0CxVi8@7{U$wlsc`(RQTMpb0w`d$Cc6_T83I5@ylQxLRPgdO&S
zH|U;9!h>0mR(mXZ&&kck9$W4fnO-7uS1HUL6feY;v*7AQg<H;*0=r5WCH;ejOce@y
z2b5knAdCZK9tD7Ba8on~qOrsZTuD7lg$orOIvDls0#BJ|k{rXVzYfd!1?oe~ectaB
z2YR!uN1(Bpr9tV#KOh+4Zs56(C5R6;2z&8(^V_{smtn{MPcO+{O)v5+!+z(@jO<l&
z=Zk!se^XnwJk-M1Zho{M$kd{T9G0s2jvt0eLrjH3TN0adA*RVwI>Mw?3{@K|zLzGa
zc6*uRiW(+iHmXY%b!v~)8onnzOYn0_J~O$dv%NIv-C;dYhrObt`(1V&Qj}xjHm_i;
zP&5WZmIOmx{R(7ZX}?{4_Yx!<mgl0d7#b@1GzbT1CHp;C5Gdsr?6tnX>5K$y4pQ<t
zeuv-3JK+&2^8=EWn>E-9eJ~c=KdWnN6<9Hm@Ae570evqvu`7Zfy#w{1^od2K+<MUv
z#YKiWn3%}Qoa?O0LEI!`E2{?i<1kj?g#P232RP!qn@55YZFOJ+&54W7V2=i<DE#tI
z-FpWze*%&0oIx2&Hv+(H#Sb!AlVxuCsE9mh>NK|L0n+3zC6Xqg1$Q&+IyXT_!R~6i
z93Z&OhU4Yy;%6(J%zNIWxean>_3Yut*4NjcnVqEo)JZ03D)ia`X|I*fw0Qy&#o!x7
z`3+9nf}(XKu}1lA!nK+tJ?&gU#@R+5u2C&k<@f~p7yLTgpW}FoyWHgE6TPjY(NK2b
z>8m<a=%fj|Nn2pX#6YUAo`ApGKA>rqq(oaZPD;<f(8vcTNAZrj4VYdyUwjIYqX9Ub
z#DJG^$2;N!Xpo_m7cms1pN~rI3iq+Sr)Nu5y<_7s4K7)rX0a%1j11%m;Yx_lO{%z*
z5a{F=VppIer{w8wM@skW3W4wViH%CXWd%d*%8DM8YwS@*Zpt2rM*{d0SilnJ-JPv|
zlU&S4G;!t<;F(`sw2_2PZ$Jus3qoL)I(nc}-6941od;-xZ3VH#)Iqqn{|X3zh!j<=
zf*qzRHQFQhy6F{?>}|3S8*gMHYe5Dk#l;Xm*aHO2nd#{Qnnq$k`gw&DQWO|$jU=QX
z1f?%clT8|LRDB445PMFcUEvBbaOH!)F98G4$6VC;rXok7M&5u{qt!n47nx5&P#WU{
zmuf#ELY*O4)Ev{}tC_7j0ybgJ{j99Y@I+zz;<YK>y%Qjm{`f&{m>c*!w9~QHaM8<(
zkwz)@^f;80-JeXg-oHDsUGupf*V*ccfPrL&_1CCoqmDN8+)r5v+0#GwO5@fKWbuU!
zUZM$w%SPyG7*e6$+CBwYja3<M@55aOvWf<5TmrLQR$2bE6`))lC`vY`M466JlUK{Q
zr$6`==ymOq3dnCe6nvud`;!CTLI^u-4$2Ch(P1M|nMqkix4QOpZfrl1IH6*)p<zEQ
zDG1)Gf!H&9fUo*&Zn=>`5U~<0eV|A_>A3tA8f|_l2CGfbOr&CVV?L2areU*v4l!DN
zvVsjIqAMgk-SIfi&kLxIuiO&dN`rOA9}y|sOII%%G=6RI_yW0g!|2H{yEsLN93qhU
z$G7i+*kMgsl8T%f@50Ez3Lu|QMT|#dhMToyw{}V1et@h1b+#HLc#Ra}eABxz6Tju^
zy|a;h#|zu#0~tW<N1M+2J^0fDAU0vy4{l@%ULyaeqZXh>L!Fu^pd^oeVQ!`UE`O|~
z$@T~oq~SG#24J=O!0`195oGqMyp$VYREes>+I5XG_K7sxDAWnI7*)yG3p}(BdCX+$
zy%(q!0FmPCHVPb#dM#lvF>ai7%{lu>@+n0k08x*pMmWUe56?n>)C$Ic@7Wup=TcWk
z1`-o89l1LM&fJOBrY;$p7xvGf|Fh(aE}6h$z#=WJ1fdxL1cgE=f)XK6Bf?-Iib{P5
zrbaEH0lagwxZ`R?(z$k~S66Mq$|YtwGrFHqDm0L{u?VPh3%{-H^kFC_TXL4;pnnvQ
z_^@%Ed!Jq#3PCMd7|rGYZ7%f7X`p<bR+1KxR<ciQIanJ*fb)ZAz^dkr5(V<nyX5y3
zB!PT9l7vnn8%hU1uqE<P!(EJ^)5a;#kKQ#`C>bet4&j#a{2~n4K`936A$_H8(hyV}
z(k0QQetsAYMZQeAkZx0`o@+ozvJ7esu(z^N#YTYb<_!};+QtcDT@Vj&1~-EMXN5HP
z#)v)v>4<vmTSRAe&A}XsNq2YTH;BLr-z}Lmio{-{#ck+~t|1JjPQ?;CJU51TuHg_T
zRIp};+iYpa8`>1TdkFgk)s0jlp%l&f^&J_G6Q~tSOcikP#YRhOGU%z763M^PMs&IE
z3UPm2WhUNm56;YM0MFu=bC@J+v;=9V2lMxe8h?<5{Dmm$4`nx6lrg04bMc*SLV|V*
zS`r|pe*m7nD0LU0g<TP9c2n}{XE+q380`3xQD$glKQ3Cg%mM0Bp2rRxljCY2DqI0E
zI8TTb>=vLTj64-2IA%(ky~_gWT@;QUc=KzxU(ks-#Ji8oaH$y+fq|(f5Gy=^fc8Ub
zP{<^BRp|6PTFxKBV<V2lSBPf)t*N(&QeU3Q4V8+Nn9~=*$xsJz^}G0#2pNmPJo|hT
zP;4FVqpn88IjB27=!-5JgY!E<yF#5~9b5d2)<R2U0X>y?Y3Omr5C_;j)HybtcVIIr
zq>PN+#G?FnWC9-|L~$pA17aUwa0Pz=s?QBwah~u>8dFdHfS9NC2jT;9*3lsHcRUs8
z3_+m?1xrR7J%AHz)nq>`q(<g|C#WRlRty4vrznnUUt0w$!9)5-3@-pRyxoKonjn_R
z3~Eq``$A3$|Ds}uAhtk>G}i;dEDVhyU7~<7lH<-jBnp>o846g`u)}1eePX4}609{S
zU<f1OX3(L$A%&iULJ~L^i&4QXXaS@)ITkTKq{xd@vk-m|73i^>h%)5T$P(+U;4n#i
zv7b;A1^=!<(Ykj&v9;-x$x=Uxs5S!dNHDFVf1-)jK~yP;!IFiTg5gSkbxsh%J?k#b
z;v$JaWq<ki-%9Y(zZ>6x8#GFJ85y9COggIo<2iny&<9I43manc<Fa2gAbT!H2)Cmv
z%o#vWVANQF_lviJw2AH7#nud&B>YhxddT6y0)#8!|NIV|If4w+CFrUz0ESL3F<g=h
zpgN0zRuXIm^wcggiWF`~zRgx-a3gCCDwQ4^hG+>65a>`vhcF??{uHMf%p|qe!oSbp
z#qAHEq4V0LF@4<T|M&ZPz{qWB<X4}tk#kt+eJZ1Nk20BP30mym&#Q_k!3>x~+7P34
zc3I!DCVh1Vcr!hK8UgTu_n=U%@{Nq9MaO{5N)81$%Yo}j83ZICj9CR{V3L_Wg0KYO
zwJYcdu8S#k6_u}tEdU5W{P%+aZ-Ba)ABhS216Z=YzkmTOS&?Ieo0O|^LT<J3X8HTC
z=0Tq$WF%b^IBzq;4W{lL5<e?saMsbWP*wwA5iF2>*FyF?&E$~XV0oftpMo!_!RcbM
z)(7O@#Ks;!;16IYgjI=nMS+v`Vg0?PbC!~OdjKJb`ro1$LG$04`$F8*D{K49D9Jod
zZKwI~W>bhnaZLIuR1-`@J@Zb6Nq82~bsyiC@5T-~wjk#gw@PO=%-GouT$kPiV#CQw
zStF<nQzpTI)*cz3oKjGC{O8r;!U*(-pbus2C|uG|5;!{((x_cKJLUb$tzqEizo!5N
zzaEvAcD*yn;=)>SdheL&7%A_RkQIJ4+OA((pwccnAx@)slb^mG?#N5r`g^6#l5~o~
z3-|i1gkF(;5fy1*x5nzF^|iaKQ;wrlk$ULKkNgJ#u9>nb<zh(}{&_UA*S~Q_N<r1I
z8j?CIY<f|U6zLZ008NUTwbdAQ)8r6w39{+WkLe8=D=WEnVyLlGXqox+9(6U-Wc_c<
zA<kC6Nx1IkmGy(wLSd+aAO!iblNZcs$)hsWzn0H>3>1G)Gx{Oxh0Y){@>zZ6=g7#A
za~Yw#0IAm$)xK+xio;7+U{viPnQ7v`2h|+Nip7z1ob)>ox(wkLL=q)<A;C0gR)(2P
zyQ;-i&PX;QDN_OO%t6Dw_5&ElsC0HP8~uyMa>fl;#z;8veyy@PnbZ6zRvWtN_t>?I
zOlB-U{qXq6zdJxGR+Zv5HABs?8Ig0YAK{6*3cL^_p*w^VtgObXe6YU-ctajGx$zbH
zv1wAl5tH6}=hV`^8`&UalN~O7`sY?;GK_F|V5P=qe;eRTnmJH*jZ{&G51fX(xyovT
zM#F1ph;nkWSLzy*W(B;}X7G`Yb?sE8Mo)?g=qc?M*3y2?&+HW0QJ2iRxDg(-c*v*0
zcUFl-DE^HGiBFasWjbX@>QTAhY-ln78;`NL9;UjL<f*SjcIf5SsFiJ$eSGjPn5-^o
zSLRkou`$rDi}&R#CSWdY24%KzxD?`cGL<fK+!!-Ztwm%zoRj@Ft!kzoMD-hviNcpT
z%D|$d#EEQ((~x#-SQI!#8YzVwr})NuJKT$XdPan#<8=2K?htoy!L!K`6(`&#bK7Z4
zNF%d+`Yr-5+k>R}Grz#P3CjJK_p#AUnq<B`*Jz?ckhDF*<!t1CH)cpYoEQcjXuG3|
zMmYQnaKI0Lp#LuS|DP9G<RYIoOFygONMpPVBoc;!zFgE#;i8nl6c6lwcE8osx+!B%
zIFo@ir$!9V4tla$<qt33YOhAb%mQfU#lG9}tRiW;a&hrS6<sx^1^zD~ay)-yAu3Fr
zVuT*GJSJ&<K)looEjB%N#TKsh3F@vzcd6Q6ScErW#adU-<cr!J{-OBF_q9nIb@vD+
zuDsLCsG1R3hN@+Tj>4Bu%o>VA4gX>&o+LMX0zpv{xG6Sj@*}<rj}jS3SHej6pRR<C
zil2N3WUu~Aq5lwQW27itw_mk}Q65&JtGgm58elA4iArJr-EgpYFriY3aAajMF(2ne
z<zosb@v#sZq~tmw)v?f7-3ODNGeb-%{sr}hd{pT*SQ!?Hf=^jyO{VExjq2%zY6)(&
zH?lFs5&S!D@}hPBqB!7n7D0)eyeKWEF11pTvEb%l3hbVIKNFkea)(Cw2RVUihsFl2
zgyAY_DeQl@bO|3SwF57==T%%sWfW`VDlJx|+Tg0|S>FIXC{fmW;;k-UE5%zaUzWu-
z&N<Ar8{jO<qC)9Si!ZJT2YT3uq+2l1#jbOhd+2eEtQHw;>eAk(whdqh%8ezysx>(E
zjcvsnNA2bL=&bVw-?=P$_cLp~7iF6h1sH7a$wT68L;pVYzp>qtT>S3>g1tog-(o)~
zf`iuB|9Y|OJ52TP-A!?|gGY9q!#uM6*PB)bc{n~NK4CUIvZTMKJK2ML6Ko7=%GP0O
zc;AK7BHQlgAHi)Z&5nuvlvOe4pn%_=nD4jP6qpYQvqI>%+IPJ@A1wo&f&cFSR+S0b
z!m&yVT=|TdC1f>oLbg-|wp`mP_H}=}u0+4c%=LM`hFa2$MSpE2(oJm;lhefX-F{S~
zY>h#vzA=I79^&>I8Bx!tqnQV$*#f;|{Bl#H^>k`9(1?x})pz`ltzIftaa=RY1j$NS
z<uvB7|NYpWV!z>H;5()^0+#j$_KZGsmP41-Gazu~u&)53QA!m@O%f|G)`MQ<Pnks;
zUgNji5fbx(1F14%)2i^fGedLd-yt149{DjvD)iA)VkKzJ!_aZ8s@X$b!avV9vQODp
zZQ0X#A5?xS-Rv%Rxf_3q5Y4Nwhm+lRJ_#SJoncWEr>&mFm$2?HC@~0<)=Z}9Bl-J%
z45O5YSwdo95f%#0s4pM;elGB7Q47<h+_Jt>V;;AuP(~}JoR7LuX^Vg{D@`7@f0h7b
zIY@L7r;#<Lo3KUIBxNG1ozW*QpF#&U9C<?iFyry$zoSBRr89PUvl`HN<>oFF8ko6v
zm8G>@AfPRqA%}y|43Q)FyZ-lJX302%BH?3k`S7&TTJQP7RKn(Ny@QRd;ItN{mAD(q
z<^o#d7MKY~3Z|6JU|kAy>Yo!WgT2rjZ{KGa@7BJREJU4RP<1CO*u8KwHB{@aQ>oc(
zaF8<5yO8NW!2KuX8pd$`U<nx+WKykSFmvu(b=06=*bGd(U7G9Zc&gl&e{<WGlVelc
zCpnH~cn{J0v8u9%uz!X^&z|zN1o>K@+Lqi5BB@Ob`IdH%b9oh7La>~k5|Hg@*ZvpY
zh2<KjV5u2-7o>cr*5(UYk+0dvgV7bJaTh8Q?>-+mVe)!a#oj^~D}1TNi9;qK=<oUH
zDQTuQSpX-UMYVjwCxq<cHbZ>mEUEb0-Y|ugJ=6<Rfqj|az?lrL3E#yPHbd^;bMg#x
zLmA#Z`_>;aP|!_dW_MJx0YhQ!A1-487o*9|G<~QgO0g3b=8hMMC3Y?##?0>`XGw_v
zT$Rmzh=C0M<x@|PN#5u1@*+_il(2Z*b0~UxB0I7&>rxI#vC1h*_!=}kL`@mnR~G3g
zE;Iv%V6d@aw(%i3vk!_lEDos)TT8SW?j0&Rx6AO}{B~}O3@J=t*IpMFTp8w8CP{4=
zW!h%rg5Y;(ytb@q^^3B!tDp7dsKd$UVPi9aabQs?g*vxhotCRyhP|M?7xABW5%7O#
z62#&R=>Rc<=Ho3-zWwj#3BOXs_BDBgn4I@NwR~<Cl4pLpsQ$=&mN4j`!vZtJeEmGS
z=i%Ef9oxP`*MbH2W>t`!=l1pawr+Fe>x}s0TC~V{Q?RxMtHMPvvNB~OcUDFDomBRV
zpw|`zYinHKbSh+l#-@#u&TbExT9dSR2(t}bxm424oNFC3>Sn>;bdrTNIhx`2^%h63
z`b}Zr(&5c1NM?XjtRNBhV{u`b<T1Y$yZH0JRG}SA23o7uHuUxV@?Z;0sEp{)-oLmD
zgtQ%k>^=GaBesE(-M`oHchXHF)g5kpJ|F^yY9|~o+vxhmgtXI!I6<I*sMM><2IEWQ
zZ{7L~y-}871Fiq{3xj-33vL{MF`=ctOf1ZtV?fdaf8VmGd1&n8P=Ed~k8pu?++#%>
zM}1V2qOIqd)fs4#-NE=>b2K0Cn82y?ZEtUOu~lvAHsa5}K-v<AUV^VnU+n%_O@iWX
zK6;SW;dgUoLyUSx@Ju;Qa6pUn-#qk(LBh&0GFD~)cKc2!r|qR^eyDNJ*ICY}3gyKZ
z-DPj~tM8hah7>cF8;eJHSDEn*h)wurUq?OzjS5V4Pp(gm_JxcTXX@zI^&`{;jJosc
zfbW9biF>ou-P2RHV&{p9Zs<q&R$??mC9EjmFc^07k_<jh!d6q!AWXQMriUxndY>?T
zR}M;8T+`E`7wh`J$vQZyspJYnJ<J~w{!U1XW(nTNAFW><b<orPT0x^_F75v|JakC8
zNR?wAWF`ua{I`@DhOjPDm9_?g$Rd~Yy|baJ2(<1KhDIH-bmcOKzM9(iSn7ap2_eHH
z4t2TLJtft{jA<fY`jAxqwt#L!;bJL^^CpK&Wh*32KmFVKB$nM1oM3RSaU2+6HR2B6
zBY0tDKlJudHc`PFM>WWqjTE<Rwx_0Y5zRd0Y{FiBEB-)H(HXCK5<Aig=AxOxcz~Nv
z=-TperFbNIIs}I5({KL7gvjLZO~YMPp_dBTj+7a6BSCVE;D4#9q~fPW${)Y@BZqC>
zlJjx@$MEF=D<5}kIx0?Q@#*sXhR@Edyo{ggD<Ni~fW?2BBQ*^bzd2%I-XvRlK9p&i
z332`@s$l9E%XT5X5*D^h06GdAlG+BJ&81#DUYvG+0Let@`nIpw0Nc=wdo%9+6T!pW
zNAFJ$oX3G&JmA{3F-DZH2SeZRj=c5Dv5Flr4Ft)B3JuUfxv0f*yG{KH@htbzp9<-+
zXal!<a|-quAusDAv%*f9h(&_C;Escb&Kb8;iRZGyP0tkp{~1=>U$-xF*!2!^%YB1`
zgH2kZy}vhL-Rtk0ca3!OXap`FF$x(R1pRK<Z`<_U`pXKPGw72>v5x^R$JvOkW-RfW
z1O3?ZI?BuTWns91TWL9lhCuB29}X4*!3I1ljbq+7HotJAnvu)qULVAhX5>3^yPaoP
zj-T|r_|fCt>Bp@frL3ba;>y+jN37a!e*2^A>oZK0_N&v6kcD2q=>9s;QW6w04$LNR
zR*-n4))%W1>t+>_4d)H3dzEOqZgEWgPW_1j@YJ%@KJto+ToL@MJBek7-(jH9cJINb
z&x)(@XL=P?aTuN*J?*^_U2*X}R_X4ys%rc~orA0Slp#F$DgqUZ3H|Skgz`g3jvNF&
zSNp&8@^5&#`9Jb0ctjJl+PxE8QwZX#qXU&RpvXr1dts@nuZvp0Fso(R0VF^Tcv~RA
z#J)ojke>+*?a$UwUFdkQXJ8`uPI>a<c%I!d>@mo5v-U||LFhKf+FY-(_iMo2AE%?!
zML?8>-?g#tD;!y$<iGQXOmv2Q$-1J15Xhx;Mg?f;iDH;kmTfBP2(79Um2|`5xpvEK
z%<=_4WiOUjq3e<_e?(Zj?BQ$j*xrma{#i83^*4SPE-xSY(2IYqEmrXwKan1_zV<Rn
z=D8<0JgU?Ts#Pz*s9I%ES=As*nC2pnIp9#!^#m}e|5Sa*%2a3H<)nYMYwHA)9W5lf
zwQ%0Y!9DD|BB@{zC&MCt6`a9p3b6y)n##_;0$e%V+dj5mH1Xvc*uOTyQ|NcjuAmDY
z)&uDdeX+P9MX@#}CVWMsQ3;;=x))qnTM>=*p15%E0#_m(uqt+l{(hZTVLz$sC55KQ
zoq{p{rt>t>8WPkCPkDYqHbwP3v;{iX?Ub1!e`5;<jx&NZ)5NT9B|mtXgfXx)IR1Te
z%3tg3yVkf4aU0ar%|#O2A_<CWB;eMhRzK3`Z20G0;Srb}fri1eBQd)K5cRy<zoK%E
zeXpo&R?+#NZ$#EJQ@4UyDL<R%1v?9r1E^Up`F(%pn-T)A_^nFerL(vSTY!yBIngaO
z_>msXt}Z!!VbkXrZY>qMuzEW{B@sNha)w0sGuvxxI<5hO%(fKaJ^<nP|2wC`OI3C}
zv)Fo2e<ecO<OcFhcC$kqR}E5HAVhQD$e77?0ks2VIxkGG`}4Sk=QX|+Nhw!#xb#k>
z3+vavBheXF74i&JaEpzH;Q-zl_*MDyGzjU@Mj89U>dCk2z2QQY2k0^KIYNR^tiS8}
zAA|(_`wz4v2oeLm{_}rZ_y6j@|EFFQFNAjyuKq8D%o${{^?SW-T<Px}iALlA6hf7%
zmIM5j)4I{I_#0YWsNLDT*9H*5-~jF49a7>c3S5R%Xw~)zTq$&?OT7S{O^*Bj+<uuB
zFdNd6Mw}h|-)bo)h%JF1aBaE~K>Pyuya^SOk^H;ZRRTkGJG=ed4lpS?Y)LH9O^=Hr
za1l_U*D@pUub3~Y;RD>j_5m3Q&Ny~W*8h81hMeuO6RD!6mRwJ8r3frx!KFJ4(FkG3
z#AXP~Qvc^wnz6#QU5>PDTC10eY2-3wKmt*q0roCd#v;a(b(s_;hTP^ct9T9Z|7-25
zqoQutb_E2a1q1<Ukd$!fPGyE}QQ`&Z?(UH80TB>T7^G9W1*8>0I;DkSNC63X&olb%
zefHjKo$otmt;1ienco_i=NI>NU-xxgPZrGuqQoz!T)A!EV$&LPde6S}rDxei;q0Me
zhBb^prZOeumv7OIckcMbdf^CLiP7THylkpy+gIK#{dy%tXC=Gu!p_FHj3rUeL|8*G
zkU4B>?bW!f$e%UHJPpkJGD@9Jyk_Zcn*r(a3!aV^yttsZKA`urRcS+H#tVle!$RO~
zF`c^gDvE)x4qR&+B-5EzV=Cbo?jp!-`_zV5p*;pyncVvsLo9{|+E#GA%E6`i-X>@(
z;e;^f!~Witg`hIG=8x$j!&$Mywz0+$>v!N|pOyR(Dl`b*yNJzghvk_Da694Sz=-{<
zva%3(3I{KBL;2mI9mSxvd|d4B!(1{h)>m*=q#&LZf3TBSoZ9$F(95=e+bBY>%(_^(
zXVU65=<E6@@uEQUmAXuiq<=J-?<5vEEC85C^D|j`^O^A{RrG>bY70lSPxiQ~9GNQ~
zqA=SdH^uDicT{{RFVE}iU40dsKgOzZ@=OWEmR7ygpFTR!zpfnJkNDLb=Md&>luLHJ
z5~6cH>l%M%kk^-zzmW3Wh&o;a4jFx_>g!$xHFVm@M072g7@RCuB(g;+>H1G;vTyh}
zJ8nNm5sphVbmG7#tyI5D)xAivui{BX9J=jKjONTtF{;)Z?ttAW8iq`8@Tcv?>r0$Q
z)qfLUI5t1a`*5;<5!|LpjvXkD->KvkOfw!2SsT-e5%hqWmt%P1Yq09IupV+Hg`eEo
zM`t{GWM6*JY@8inlMq25+*tzZL^zmu7}Hgbv%E^=t8%|qd+rA9JVP<_lvO}no0IIS
z@3qpz)62j9GFhPgY}*h~I}Po8_q@yMT{7$FsoQjW1R!3XkF3=!4fw^x2U8-iM(N&Z
zP$yj&OcFy|efO7!gnhrgE|W})QaNvx?j%37B(r5merCKyc07@)DMJR+4$5LZ<V3=;
zN`@9|2+5F7OC`B?F+;(88sqn6a$~4f=Q-U}%%NRePVNKs$!>f-e5t`8%G4azGCt~t
z<GYP75N^J)D+K+gPaZyX?3=70_aRx^Cto%(WQDC25*-{Ztj*CN8?`N|FL|F@zHw=P
zr<3j1IsBe{l|h$38iPh!P<6_RL8PjdBv|Q>km@iLzb@jwBo&M-7h38uxEihvq(6bn
z-D}OE_$3f;c>a(@y05w~Aa9Av1@1XyT&9Vr$T&pmQAISn_-GdDzG8@NvZrp1W%X^y
z@VC(HP9L7M9zuXxXlM3;>S;*{^%E+*&TOo}MKb(`KGjM)aizje*Amxn(AXny{~-3)
zLpQ_Pb#k8<2&=1F1-dcnHT!dg>-y?XcqF#RO>H86+z)i_UrkdjB4#<<<d4}Z<)O@b
zpuX|k*V2IOy{cOX;^h8&uONP-wUtFz+hF#xPd}{;H}}oPPv=;l((sM0b0!!X3h2M2
zQj7PDZZiH_qeiE`h(ijI17Vbex0I{al!kaDQ*^i6O#1OM+{+x=|L*wal4ld83kWNU
z{uD1~<#;yuRz-?>U>?qPw-WQc$R>fHAb5Om!C<*Y41&XZ)H0b_!$gT+Qv0iI{P>JS
zIUX$0kgooPf;&-=lY{OEQKPqi?Hpm}Qjh9^o3qBbyRCfy_b|-IOWpxMe_OKT*wSQ=
z>@fUY2O>j_#sfv|!ec)=b~ZOB@umgb0>Te|ziJXrJramAGp1qTb<vh)6nD3TM!%oL
zCA^uDt)dw5OFg+-Suh9y9r=&Mi=asr{t3!#!+ex?l~;|(Y1sU+#ELE()mzsg9qkey
z2vUb^VQ5i@+GhYhO-eXAF;IG1`tWNS9%;HKmY()XRiAn-exo=I!}a@y<dI<a2G?&>
zpIOao=i*0~;phIZ>e_{2FZ+fC3QrdB9MxvLmJ@~XGN)bJ-<fA$T&&l{Ma+PLKi*q?
zpRQQQPtP8A&N6}C-;)5`mEu*?uFT=?6=pVu`v+h4n5b!%Ea}DQekWpB?@EsbDP0%X
z#>^&9E04WqKHnPSE;JVU=2$wWuKM@rC~t<2^|NX2X;*t0!I#!X_6xt3eIqa4P<!o*
z2DSYX%vqYTdt?ee_W{jGs2x!xLca+0692vp%>>lid1HoOY}Y&Z>3#qapTTHV<s?T%
z&6YRYbt8VAZmCjN(HN5k%zuilOqv?b`Dx#o@RC)z-_4slylL5r*;CSeD0>>(N*258
zu1U7_wAT*?|7d%hOnXS92<fdz?2Gz4I9NhkI(V^plhX;MO2$_9>HDvRn@c@E5BfeR
zM~#KB+P2kU(C5zARE#~8OL1ZeC?~vAIqhMmBeQ+98}So&`h&|2J|nq`aJf~3R<jCp
z=)%!_@KuF$$9kg5k7ZjK=>upp<AoTf;nkz&>o6o*(j@*`m3S<XI7Uz;o5Axr#s}+@
zqwAPfkx%kpPPb6vRIDVj{Ys4!Npr7-Zm$Xifse-S!Qi6m)rQ=N@a6y2|73t$Ly|k=
z>NwnOkrNwXWHY}?sU#3KXh;a*qN5|Vb=tI+B)+2T;0<_*o}SMVQ4m7TH9tVffjigu
zjl~Un1mk=n9@F)2-NE-iZvb?aipEyj-+vaGx}=Ky0VDqy#DRAM!r8QmADFZmL`Ft}
zI&ThkC9MUj`{QUZ3Al>}0{C=UP&H<oH*dW+cKS$wWE4R%9|ZVZ-2aZ`{Ra_(26z8t
ze*g2u{}b+bD~0<-Q^p{;!#B;pe7EDtr6M~ToY(zoUhvxVyT)VsN^*CO<(d?gyn|Nu
zM&SgG>rNo9a~Kn+)#Y2Z10PAf|Ju$jph4c`1+-rtO}XEX9@wr;dpa15O6ql<q)l`3
zS#G*FuTp<9*`2X6UOqgZ{Ds>0{!8boSuHvSJ&F8oEOBA7V{}D#7gkHjcfzQ%TAAPn
zM>y6Nd5-A3vp7)qZSE|RyttAkPsIgiqL;Zv9cN^*TnR72k-{F)ydbIGTV*|12pObo
zOPjSwv)5}?eB7_^xmV$k^4ObbB9bi8?vWpAOSyu>7nXJD|Hux7SxiQMuNA>G&MAcE
zsPi4$Fy-;HZIX&<m_3~wzgj%Zaio}s$@hVC>C^Rnu+C4!9248mtDIiLe?Nhz&F|z5
z_4knZb1&MjRPB%G)HbEkZcke8-IJ<fQ{Q4PZ-M1FN0xmO#SYVni|FAYa~V`g@=VH)
zSQ5+FiUtJuUZ`YWbzu@WL*tcj$rZCh`M1IXHH6SRW7cbFywOx2q96M6M=`Z=#&b(>
z<G}j5mX|S&%U$(^WHmCahNBlvlA_;qBfV(Xq^8)Fw9}-t<GI5RZM8QgSV|Ee;?i(e
zic94&H<^mJY14y@I{`z3q<So-C_z(LW~N%h<LpFXd(7Qw;Rh=p)~{Astb5U4Z%$gj
ztjS)?oVy%s+NlvMY%0H>X4ruOm2_jNByYZ?Tu%a#+d63HPxknwQPuVYNJo@l(z!)J
zm`bzAA~YrOtSVbdu=M+&U^g?7v6XYl*o^)u&5Au24r@7IYBX4<RD@Q-xv5mQKNy@a
zE^Y?#PdEw>4Spu#++MiFfFe?c5<oY`+uj+Mej!*`E|*Dk?cFP?oa6<)nB9f(UIER|
zzDk^e5f}L<Ny@f`-duUC=R`zM>~0DBc=KQWGdH_kJ2fn>-m{wYkp8eBfwpEw{S$!1
z3_E{&h7;490Ye0K-LWA*hL<Wy!>H|2dyC37coAIw{SaumeaTTpH36wp7}iZ%!+?s?
z&D<_%L`#w9o@i+CI~~75HO|Sv+GkS3Sxsi|LrP<e$KfvcgK`0OTbC^g+Ra9jq@(*X
z(VZsa!rykL@Y;0t&gofTG<^H-Gq$`Q*uOEEoAK`B!g_%e%9$IuG|tUQE&+2KnA*8p
z$wYVDnEPKkSWH69;Vad3HwoXeej$5#V9Gk>QVH3ZIcmWvfq0R-h{+^c*oJ8{z|&Ee
zl=P!Sx=rhR7$=9({_jFPt+4jp81C1dRFT^1nmxk!;Y3uLhCh7-OI<F#=^j4tCW1So
zY&0uoZ67YXD)jn0@}*`qIAaz5HTN=Ta1#+fi57+!NsAz%ru%7mqAS8Fx}J8-Cxb6^
zE%!?ZwKojkMg-g<a5c;y$HnNUBoVVvB~@4Vbr0TcoB9y)S*aWs1m4YZg@+247{FaL
zJnPyZ4`OETi$(LH(1m#9tRHzvGH#32G5hjdVq@bn?Lk6@^-)@1#UhoxKfgE?@|#QR
zJM(*DuUn*;Ebu|i9O>hQt&s@e-C{_=*_JP$K#CFd%{sF0(|LCfpOZI74L9R$GLrYN
zNTWL=LS^p6bi@`cX7K0J#h^S}wgL$M;7<`{y-4yB4Rr*EKN^$#W%8JxjN>m8TNJ{l
zb1gJEU+J`kJRPnq*DpAz^)5eW8q>MGCG|P;zKn}K)|Foo<il{g)W&FGIs4TJCH!DF
z0s&MA`M02t^q{*(ifEa4Oah&smZ@;QjP~JHmz8n0KA0f_L^A10gW7g`nBpcTL(FHp
z@M@rW`<oj>JmeL_Toc5FCr|JwPAg;PR%}AkGCQ8A%u5`pL_ej#V~~{?#u@Kg>=*=q
ziq>hEAvNpjAE1)dZWu^gGUP&ri~yW47Z%C+5hN@{)?+aw^~nKF|6dHf?aP`bv}qJ;
z3%ZJyi?ZoH%F6FD@0@f!$3$Jl)rc1zTwI5H%!QyK%Rd)i8QDxemr#xB_~|%;Y%=*;
zlMo=~=?^3=fXLz)QgeQ2-d+&;Qh8elo9c4CHlMjEds~+%$IM&YG*?IBi_@G{f0PKZ
ze#?)BrX1&f6&ewU=rm1(`j`N1(r}<h&tLXwp>@iwb_eqwEc^P>w(C!VZ}jgj(FNyO
zjOI(*ReVUkXin)c<eIDatN5|FKX-rRl-cp+@CD&eZ!dVh#RPj9pLrx@D+HyU_7-yW
z2oCws_T5xH&Zc9roNY~)Ti6#!37G9VdG7mgXt*h{B`Edm-VniV9cqixbGnFMNnmV~
z_3O8X(H62te*A}C*F3&&2##An4ShTRg?l)yGKcdc$O&^lO{aq#mfAObxx3PwGfZ>y
zQA@%>4MY<xMoGdI0<;K_dKXX>547FhN~KQz5Q3ew=q*Lat`htg*a*9UaXU?)Z$BfT
zrb~7{Z`jega;vjij{PwJ8_$Mtn7(3xdWlE|sfR?y2{msI!!Q~Kl%vBK)0^(s=VtM1
zZKrHp9n5QWVoS~&z|GKuVFFOx*I75l7nT{0>9zT7vNs}{Z*2-Uw|_5f%jSgjhb}XJ
zlmrOj3IAbs!ClrrY|?)g`){nyxRiV*4-^y>(2cy!81rnK?|WZGAOs?CxWr?6LCo%}
z0gwf%VX?X45DUnywtE`#Zp&YV{_mmkZ#E4LS^|DE<Y=zw-@wbi9x3Vqj8V#3T3Vl-
z24sNH<A>=ypfj$!Jo#y5720i)yvH18qNX2cQMJk7U-$lUx)0pq0At+Yla6<kVX}~S
zq5Ca)0`%$BzwuGgP+%!?^lIyy0+ymWc9(?Tu!9pO(q08N@Ay}&Aqubt?Qg6xljmI;
zgygK!F&X-_Rr^ukUBHt`sW`Ho_^-aF0Zbj76(YIoG`TzK_^IvYr%e{ylBDU0c8y-n
zWHm=QkiUKUX@{J;N7bNcmY51q8WtY9^ZU=^>PVoD9r(0>2Q=NYk_uEC*ROe;HT=n3
zB1lS!H(g11rEr0*z(J?IEN~HWyFBef?#+knhi#F(cm~~?zVFLRY{+-0d_U{fAD2p>
zoQcHgyx)kiN%DY&Pgn+o5@y<T;4wV}E3Pzzq(Ru3=1@xK(@oaUSK0Yzkonn{A54Z@
zy}@em0A0mzoY9HK858Z7Bfpq0mBzM>7U2_!&m#Xya`Z)%-SjK>XGLN+<YarDH*GY+
zHtw3$SnK#YlG{$sVf+0Uai5r)$T*;l+2a1#o2!&&xLX@7P^F{$8)QT^9(e}_vG0yL
z?PJ6b>pYjHb>UKwNIaxR!dW<n7&!aVdwvRX9_L`3dh0E}ca)_8Hn`Zz$u)li$25_u
z6tDYzPyL{1$|GCGm$~^v#Jq1KPzeShJJnIuF~6=7iGKy{NGMIz)9fG?O6YX97b>@Z
z5V|@tERncd^d9<F1l&`~vOpO&P6jn4SiRa3>qN#l;&;cVq_AZmTQ)zI{8IG|mS+Rv
zjouU+jSNuNRGRG&G=Qftse-mbpCrv{Qg`cER*jK2yn8O&EiOrlK&D0x7*yGs%d_MN
zCXju`L6R<^r>xt;XW?(1-mj>6IYD7tkAK=YYsx?K;mX()LTSIql`P6Fo%!{pl^u;f
zmW=n{UgxZX&Ni}Kx0jxp(;`jU@bP+2`~4V@_aWC}WTW1Sfn9dEh{d*GXUl9!m(P0i
zLp_C8#(B#zKOxjeDYX%uI0d>IID$|JwyHD5epfra(_D;os$Tb8e+C;nsatN>Scn)=
zR;!maJX~05Cc8~A`1wJ`M!Few{Hv(acX~5-h*SSoEzi-QwC<^)!@=7sYbnyUyFdev
z03&<B$ge`ixj=yM(L39@Q3%3JKVN}B8A5Tmn62m)?beobNcJz9gg@GaPQ3rv0fLj+
zrrSj8^UjJoWSHQD6ok)1O!Tug$!$u~Om3A@K}=Z2qYrNQnd7G06!PEe*L(M%%)dK+
z@g_5biWsE*wm2Ddo0BELY6ay=k_LxQ0|}D0aJY<)GD663)^a(g1a{r>>W0~2L1kpB
zn9SkkVdiLz)0r}MsjnkY$=H{-uZXsa-Ih)0$@;9d?QCR6E1H^rwV1n%_XmNnD`oS?
z{R4q)?3d621fun|1UsyWN9)kt{|QB4S$db1^qrw;Jr_4ttrbS!s615LmUD=T#I9cu
zKpfib*~HTx{*hT12Dq4axTGMf^^3|`yf%rv+%8E?q9pq*_SukA2P(aI&!DD_k2Sw&
z)dRsrMGY>h8|~$;VQNL+g?T|{cc`50BLl?Z23Bh9n;&yksF|L<`w8~|bD9Z=krkJ`
z_#t+<HJS}cLbD+(hLLUT&txFi3EJr^iaT&}&Y@*@F~mSd1}R~qw;q8NGnisX@<h}@
z9d0|(YCRRvyial6!OEJQIpMYR!eiN$hkrWTa}K-6zC~IgVFFiSeDN-YLZ@bG)AUx3
zW;4Pr+-UAj<B_riUr=Lc1On*HQ)z$XXD77NtBc4#8bTuE`XVx#nnW?RwO;fsiiSx!
z<kOWk3OcE-FL<~#Ys_uH9|w&I+`T9FP>zpM)o;OX;em_Og9IiYI}c|QLkFjrhV6Fo
z!K+N&CaF$xb;~ePnj*;rYBh=EQbP6`^``Vr5WSYn$Hzj&t0*yN@n%tQwp#dAl7WaS
z<J?$r5Yc7vU5do8`*5$NW05ZGjN`wIMBZ39yeDg^H&<u%#kJS3AC@&NusP<K@UBAX
zk9)j*g~z<q?o-H-Ak0ytR^y4^Ydp=vmx$Uz!l!Y_R>7E!RjlQ(bW$omVVnz9ItHQ9
z>*)yS+U0SiE%(^giqUu?@rBpEFSFAJw)Plj>zM+`T*ho#U`HLnSUNAT9A<jw+0Q5_
zfX2p4S*><Ulc8eD6l&*r;Z$PN7?eY^y<0(hPdrL(pARX(K|(&g<;{FO+pp?Rdz4n!
z$fHO7POEQ?pY3VZ@&*;>@Nxz1YEVJUIyVLA0Me~TEPC>-((6b#R@NTXCk6E8et2Nr
zdX%rGFZpUV<%*@iOZw^SyRSb7tI$_JruHCf^co4Za7=Mh>Cv9GoBH$Z<w|G?zl*kJ
z=F9DPFV?92;X@?%S}J{A?PO%cVjPK>mVjbR<Ozlg@TUx3oFG53q@WGp`jtku^dH0T
zx_zB81{L15D@?5AEOi|tP))ntNiS9muU8|Vj*iUMNm%Ce1=h{n$>GbwWFF3cExZrB
z=YwA1dQIPL;v^9lZ7GztI)x5QxS!5i4~LUT+t7n8)`xk}n>uDHEv*2d@vQ16F5B;R
z@kGIQ39Ci)yXkLs^zxLPOhm*eCq3Y6OKRnE_9?DmTOfm+E3Pfn(d7Ir4~bYOpapDB
z0v&qXgs7IC9|h88DAU^XEue4w9}tQff{j%0%a>L+&o$i0+w$X404PNK#QN`ak^lD!
z%Ky(tG6$+X($*Dh(DPRO^a{ob9vYK$khc=&t_gM9a5kB}Ovt|UA{f(=$z7V;A6HbA
zjK-f~l_eavR%E~QTJG-m%3QvYx~2JHKrT1%^r^e^s%&#{x(PKMols-6pa(YFUxOUZ
zFR!nyI7LX@BfpDN^Lw)^mTsLPrGci_x+jtdY#w4kf366~j_0V38qcX$u0g39jyZY0
z8zLd=V!Hfp@xaxA&f;SKop+jycN>-5fN_!%b6J{(=KWJXM7u_tP?kjv@2HJQ@u`jL
zqQ;Lmywu_1xMtAuDl=rxwtcKIw^t%vk^P)&yog_j_y{Qthg!`Tjeq3gk;EP~h-sc-
zUU7-Lxq9?wPX{sH_c$y|H=5}3q(U?>(37a@%yrnjTs4@D&Z!d@gt?s$C`mIb(Rrqo
z8@EmoW-6}EQM_kzwF?e0Y@$QNd@O1Bq?2v8dUCNg{&VpZ8O`eZDbo2yWtKq)RB_;k
zEH+EO=+OYnM(r>!+|apQkDD3<wFsL-7$F=hLmO-UnqS`MrxOdh8IRw7=+_8%h;f?)
zhX@7c3aim4puRimhi9+9{t}|g7+@rlY4_S5lx)eO##+l=TD4SryVQ;MkNzDV9AhPa
z7C43GXm^x;ITisCbA^-B`qMTH$#Vw7iO5E%WtGlr*wCd9$5*|1gi4<uD{@W7IU<>M
zll!mpSSKVpIpFU!!dq1@yJ*NY)mG>18c*3_t?Q5L*x%;FekbPH5}q7Bfg=v<QT=D1
zz6ZE}=6<#5d?TpLbz^w{My53Im@zgkxT&ZPHm#?su)VXG0nAZSpo0eqOcAFC$oZ(u
zLSI%#RHP8$DYqB0-{e9JE$BUqyA;zS(%urJUzp~3Yg#6l?JAU0Fz!rele&TBGh)5a
z@rZ5od#}OV(dJZ5(yQ5TRkf=-GzhNK(DzVJ1`^9>9Y$RBU79>U_a-Ld{WXEE;dg|B
zbMU2MJIVbuFyoxKB**M7<BZ<<v~);5t>0QNQ!;0XJ8oChX0&X6DOKXw`B5+!>qaLQ
zYuuZ#lmY|gzWt!H){%5VK?^Ko8nc<^(t>k<4Ku0%hnJ&Wo#AaMJ|2}nanox7CT(MP
z=ChG?YVudmXBu1Y{5oj16@3tLEeE7DA`!qOYx%}P@}LXLk0Fd@?z*I>;#UmGe)=<>
z8|L;GG6FE#?FJo_IZx1cyNHS8oGv_{>%<6I#z4DH2F%#XEx@eC-ZJhcS)_e|lrh<q
zKSl`{<~LFk-J6<#cM6CW0R`t<;l(w~A5dkZTy0|kc)&B&b)$PLlusQ^Smwsq-kP@C
z?NDrZjx#ohrd7b7KGPl_U>TV=#kozNb|)|<;*KOpmeo^bl-_)0?s4w|*xf`ay@2}?
z>A}Opv)0Ye$NX@&eLv9o0pVqgnJDgAS9>|Xk;Fqn->%+0kWkLS3k<}Tehk}hEx^WZ
z%FE_%uJ*xwQH^?v&oN!LG9Ndw<j!&Op_UZq_G4ZnWg7y1FEIgF;x*a9t~Npr>dYTD
zA80wX$0#qO%?Lb%UU}t3e%h9;AT>dk%2m~j*8}UX|5U!~kH6OZtkK`m{~5x!Qs|~s
zr8Nh~xZ`Fg5R90gCv^q|7?G{+!NrqYHyt0<=Q}+QSh&ShiB2OBK2tOa{^i*WoT?ij
z{kxo+`6x=}N-pY!Jd=RMgI$opFQ}mS+bLfi8v3wPhD7G7TG+BSiit>gVK~2_K%oe|
zqvgo$6tq>jacjQOSwtbeI@_rQChWZA5E;Dy;xIOsELNwk5zBkx4LX&Mr5VVgyr_qO
zWe}<|_4{*9)$SzZm%Y6af=ldUTHB?0405eL6)a$rw?DkwQ#%IMTgZH10)vEkQCnuH
z?{%^fCJR-2d%Iuo;p)eu?B%P+PHl&ew|92N&CnU!Xv3!K_5n{bs$qT|Y@Akm1rklh
z&GcJT_}X>zwG@6l;N!~x5m8&e;|NTAR<P;3fH!SGq~nkXZM43la|dnd-zlqlNdf{$
zj?f#~R0AKHQPDZbyLcesy%$d)akk)YUOCix@PcmuL`9rPXR7}sVmwz^mE;?K#%5e>
z%6j9I%oEP!JP5%`|BKs@bA!$5@6c`$hmWxoR)m=*=!3)m!pBiVkcA}YqUI}&TYp4j
zcO^N!fNx`xBY&C!3jjTT1u#xk9Nd$IGJ|LT?Es4}aO@4ttoxudzrr5EW{WmYJp(zY
zavNc{Q%}HM@z8Yh5%^smKkD;mb<_xmHTkJ>i)ogaBAynD;jRzkTQ?w%c!5FdBY8X`
zpvwzvI9TAVn?37*9l#HN6h}Fxzz81dzlAOTNk#k*FDB%VVLrvYC@x4Gw}%>-(#3!c
z&$|C9rvNztYOmYkElC*#id2B!0{!8DlJJJ;@#FU?#!C18meSwK48VW<p}49_<R7Yz
zLD{V<uuF;!eW4N%e1K4+K~&>1FFG?D7<hjVVqg~FCL5oaVC8;NDX1`BV0!1j2ne7v
p1)+a`{>~ps>Hp(5!b|y^L1_BT^~U-uf*arm_DEf^Qr<M|e*sr85Ox3n
literal 81329
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%0AJ1PuixJX??D}PX8AKL
zjh5($@)s^R1*ogu;au*l%mcB_kBw(2nYVSn+OUAuU?M`LV<V?<RiyqKO2Uy=9oe_A
z@P(;r$fuW5nPsBV@|jy#faYWKH>D}wUr|mw8?Vd<*UXd2b?U}qHiCo#DzrW)g#24x
zYI+1yo4Nj%I(oQOWB)4z#gpCaZ8$#hhB@7rk5Ar620lcTl#ra81$t$yG?mK4Nkdq@
zWu4ra$%wSY@Yk}D9hp8ktyqSJi4tQ!c7tgz4LjG3o7Mdu?P&k8n3(hCN@3hAVPyR_
zV97;G(_L#;L+hWPX2;EsL$O+Cyh^3kSu<t`T1v89O}S|lrVSO`##0MZuJgOu0Ah}3
zi;(1vWzA{`1J%T>l&tu>bb3iT7PUZ3iM=MYBq?U1v{VztTBZ=BrQhbmxD&+;XWOK9
z($k)?GLuLdlMg6GJ0q-9hTpkl*ocZqd~47sJw8(3f1F<d>=`nA`sx6t*6Te1BVPBf
zNh47GZ7-Ce0q1$Q9o5BbIaL;Vu2UpW_hJ@i6eKZ`B8KBldyRG%3gyea1Ro3Hhs+)k
zDfD!qJpE5-BuCt`o0%b3)Sm{ARklXu>Q5^Ov6wN;&5Lr9;Rf9WPD6u$qSbz3dwmk?
zU#aEGc#Wr!`Q;DUqIKGJ(Xp7+y;04F_N4KUrfS+NB1}LS!8ua`Pe9W2@>0a!x|+P@
z>aghGRXEmdANCRVt`tL7T)Jr$LHWcymb0<x1=f)yS2vk_%8NdSaQrlk_oE5G0W4=}
znLY!!#LW+Mrb#_WMb1&1C;ilkdUg*r`XDvI3;#{RKjV7qsyVk(FKqkVG9S&Al7k=F
z($GreKT?@N0iNz<is8BBm}9T@z!XJt)80|5P@SuE1}<TaY&+6fDIIoA@!v?}UgM7u
zp22auXptxfV<*XT)%CCL6&GJ&8eq-4AN2H(p*tYYDwwDb^?VA!GW+l$>~1qZENQ}m
zgV9ocvoA~M_30@a=b#Z_oIHR8606rC8xlJqVYh3WXTycq(9CP!lP78%8r#PB{Zh{A
zSX10lBfKa@#*$IK2a_@vb0m79+=C)*{mQ*=Npbqfe)}>qAZI#GAL;1e>DE$d$8K|3
zNW{#YnVr?i{R=ggHoM+Ba}8f%63!)M)MZZ@HVo&;s2}dy7<5}H%$aTh5z8xGk2-`D
zE)5s?UWiPA2*~6%M_9h+<S_BKR`9W;c2?smy6g)xDNe{iPthjArxn?0n=qDitO?Fn
z&$f1tG+FJrr%cPl-31!<tW-$lh~-qT3C1(w$pbY^Z-*5H7U_-tc5R#OCZIswmEuR(
zvY#!=xyIIcQcytTc!X|t<jN%eJ!xvJQALcx^Mr2jXeawAy@{Yq3eD8jD72Mh=@k0e
z%oP&ow_Ud~Z=iySDq0Q(G9a~yc2*dUJ$tl)nR8+Qye>Ny;C>1EG80ewtmVFREo&^j
zE0ked0g?SL_o!bx4;Su)eDpE4YZ{517e5ZtD*O-=`OS8Z2IL64?<Q|4i(L@1aIz);
zu8TAqJ9y{pZRqE*McH(B^^EA#Vu9)DVL@yP&zPvX_=CGY69QgACVfgK$o3JfR3Q1&
z7szXYhSGiqbu6s`EUb}z?XU1-qNKn8AyVyKH~z&Lf;Dt<#znz=sxV)dj3abfv-jUc
zC;(`)iByhRep~G4dxnUOrX9X}0*%|ou$qvfnt7(tw)ZZWjhnTth1%5U_c~R#V-2Uh
zoPMgnIe>w9Msw^Zch|Ka#%#pg`IEi$EBC|}W)4F$=cq(RCb}LuE2-(2zAtA6uW(!4
zF&;duujgA*vnx8w^JBjW#LtID7zqSvOOp}ucm;F%DJON2q_5lAm}T~rDG$+GR`c;n
zWva8UsK4~GQnyXn^!}(M5?EI0WxcHi4fa+rQU-cp)dQf9KSG)?ZoG7GxVl&?!_rdb
z7kOEJV^nHq<<1;}x$pnqvdxp(0%;%dlfI_V1XxIeY*7s(JxPEAG~MKFJ0y67(%}eW
z-nr5*Yws7zShrA=$=gqt>Lpq~zlIiCnTO29%37#bXrM$-MmxwfAkT?_=@dsk*QsUv
zpN^)sbP|4&GJ;QLGauQ?^S_{j6;uBye#?T+6+2hwo_q6W6dmuo;vZ{~Fte+j($(`k
z$?wSaBb`I2<2qnj=hafX{apo5tO=~0a0;;Of4He9D^=3kG?y`$ZGC(cQ$6!Ar}*cu
z_b!nO`@yet5aa$@a!*583^QLq+H$A-wSGwbd35^iUTM2(xik_%-%GPLm$193pHE)D
zQf+}%`FGS%j8EindoJH9C4@$a(;*sWBy(&}pTFqtu@oYHpB40Dxoy_8Fj$)Af;Q2~
z)i3{x=h^$k^caj&9>td|q}q$elQPu#nYvO5abGiadBng%f;xZCkMJVf+{{jNRIdnz
zDxd1#QNp=*>yl6tm)@&iA2-MiU{7mkMUF2hA|n@vV3Eivj#?t%a~2`TuZpQ>$VM2v
zTxa8KXvm^7?mSHuvi0ceG^j3{^GvIPu8^HMYd7x^tH#bsS>%rkcoOuF5wF%^pu5}2
zTCbPC6$rr&<nI5}HpLr+=`PzqR!{@uN#N>m`FaPKiDsi(9I48Fr-`S)#hJ-XpWB)B
zTgZ9ZSv|_jrm<|1sRgS?k^N^%r?Z}2xkW}GMdz<P@?rzh&OGopGD-?G1aI(rSg;E;
zWHPN5yLB*d07firt?G^W0(QCkXItcgPln_bNK;FeN~lK$Z2z*1ar<&qOU3%m;=3eU
z$`)%3mNZUv0f;&uPN{9k+FKW8o?qs$&676M=6ictsoT(+M~IECET78$qy3vaV#~zn
zX)I&^1U}per?oC6bIyrD|K)S#ZLzNk0gmGB*_IfWMC?;rG`R(n(@!<_LQ%FXkc-NH
zl&nY2y%yi+LNopO!c<CNOXZ0u)_hUdqxX&unphQy^$Rn0i?l7tP6<bLR@_=B*_fnF
zlIiAHC?&?<pM_Wr%>-5SB~Z}@3w~P0M<?`=K_6s!UU~GZ-2lr}1)3zAli>8RUR9vs
z#wR}2&{Nax;8$6;L{A=JczX>8_qKzG+B~1QKtaXVx6SXS)_=i|_7i9xEUR6zOnaWw
z>?AMN?+X2H^41u8SP9$cqj4DoXdFhw7+!q%2HzK9GU}!5pOlz?irrRySUA(!oaN<4
zChm}qaECBzKbw=MvlD_{3xi8<={H@$?d9NrI9LHaIJ~-F3=}C4FDq%?HmM<j`ya9p
zFOyI&X{_75qX+qv0ddzunp^6)R6{?_p`qcYmh>5n$CSsB&i{O`hV|^*SvuNEv=I3p
zrUW8`-p_d{?5t#_0XWEWHVzIy9NiZpRpIy|J_6Vdqj3lM;77u)q4xS~qV@!!Y}GTl
z%)&5H2Q5@Ord`#oF`hfu8WX8XVG^~r{FVEo6^ZXHZFvkAHp#U@`f9t(K_P{=7c!<K
zw)raE;4SOxy|$d9_JUuG@b_kqw8>UPY;Dbp%$=s*FbxBle`AXVZR4=TYlEcYZ^$dU
ztyY|bS$Ins`|Orq*?^5~(<AGWQen-E*JlJ!48lJG$!op<AhqkxG>>>eB>EtD5-&u~
z>5mo>`v;QO`8#_rJFCwe#bVxYjC4>N$Z)<WUg}MC&q!=l_l!+8ds|GHG|S(?u{y9^
zxHdp^I%l1^c^&PaNW$OtJTqsRp0oY<)4^Pm{AH>AZjM+1G%#dhV%TOr_FojgiHpm^
z3`!S7A@8YOPxMd>)jWtxHd49rG-Say@dSgY8yBOQ<|k_m&~Ii77mQO0cWAC8#m&{Y
zID>AQVQPuL`_3_s1tE)L8#bsA5-<%~OWic^_S-1f290Ycb}cGXP@LZ*j&+VarozTF
zaIOx%J|5#RtbG-$PbuD^?_aIv85Qq`-qo(Aw#AvogkPj0dOrfY-v2&*TB!!j@#C+2
zuuHPF@8FOQvLBrIDPTUEB5+}J>vy-j*z$GAnIbpA|MpwMY1I5t+^Bsk!@S!;!smIn
zp_Q*xwYIlPp6j2#F%aO&Ya95ll=)p;>3CY@8PWOAGt1BLm}mes=W<wB&l$=m>5!xT
z4#a)O6=&)SZjz1hBnmecgy_rw^ApEetp|Q*pc+?w<{S<{3~8bMR}*=>u;;$LBm;jC
zZ4=7UvVfsKc*`=~aY`26d^~s76#3#YDJ5jQt9HGzH@z9O?JFzp^YSworq)r@^N#V^
zspQ%C{<;X-;ql70pw?lm^g+b`Ym@zbLqB^Ogi~~$)B2}A?Y`d)PdZ<VhTIVGKxI>r
zn&>x$8Hrk#-!GkR_UbmIx3kztd>UW=6Z?0EwE=97Nr_70uQ}}QM6NHtRd3QCJ$VUs
zE<3O}mdS9n4jZOzw6#_${HVKgRJZnwBB!QK0nsx?)z1<}!JGMb6{Gu&vj)R!qzt>G
z7mG6qJ}6uM4xxE164Si2^x!D{TV*mPHKWLGxJB6E$|b2=`yrnA=?%4qY-mzm2`};Q
zZx*pNEH7n@@NJQU#rHW6i{^3kw0V_$gQB8}YHQWRFWqTUf%ms^cAk5DDiZ$`mv#u6
zDdk=7+ppyLe1<7j^z;ib@4UaB&V8T<zJ{q;$Td!-($;9MJ&4y<d>@oK*I_%)0fBg*
zb}6zip{2bds&L-{YHi%<sS^*Q_e*&dF3HXb^4xL{m(DlgBA~u&lSUt;eO_wX$&PO7
zwPvBZXJ-|aWGrDZW+RketUF$ZE_VFo>!Y-To<q7HrP#Q*Lu_&?U3XEhuBDb7$HS=6
z2Z3e5yq$pe=)U_1WXkz*aJC)?rblk@w74Io#4kc>nQ#OeNIStdt8J1K8gvwBNRxnH
z%W^&I8F<Zlbf%!Df*HD5>~_@7O%^cB!y~HcwAs%<Z`%jYQIV283~0i9`X%(T@Ti!W
z?Fk9dc*0fgow(4}qWH9^Ie~4U8tIqfPM~P9;z^w6squatIzV39dV#WAikX}BWv>n7
z{{MaQZl{;wIu?9HiQ50;>n)?Q+@h{w=`LyM?pC@*y1PS4q(r(~knWI{ZfT^uq$Q*q
zq`T|gx954@G2Zcw@%=mpxw-Cr?Y;I|bImp9=c*P_#_w9fjm5#kVb|uMhksV#+A7DE
zmn;VfyOP)A-RRS$Zewa_M0D=hoLC0!mbeUS;@%Hx-tUH8Kh}H9B2sX@1)}jXU!Sat
zP)SwjvfJHuMh#*<<(0MQ1GIqr@sW6e2MNyfSXhVMw=IF*9}A_B<{W5F!5|~TGDUb1
zH$ITNUr+Q8s7R6Y0z(n#;egeGD{>8lwK-<oR5S+sRe`(&^{F*|=rA}*7UP$2r9`~k
z9f*b%Dj@6j`Oyw02xq<kFC9$F6O)w&S9M7#m%<A9v|Xv`5PwWgE{Ub7l6JSOOF`61
z`a#qUWRz{9Wh?d_OuJILNY$71$A5BI5Yctp^oNFIotNXFOsxCIN%>Cm;`8b*f1R}+
z8_+RnRlGKp5r)zE^+`=2JqdUxp^$z2xj93Si>s~wk|@DCMPh3&)Bxm0DeO-3A0g=p
z7P}lhYo;6|cOZAOG%1a&oH98dy0~UcY#`X35c+XD((s)Go=^-a+&n+uL*>V`rMGkv
ztKN6dqE_}wn<zm7<iXkYdda6XGGQ7ek;hs{TA#8z@-BlRBbUyjkg`Us?oZTRK9272
z)__a4boV7Qx8QZG6lv;s5Ia8PBB*H1^m-q7K)l4H=NerUqEvI57a(?qLywQi4oNW<
zVUOAH3Io}%`c9;WGGL{npH$sqRS_hOX+@jASA<L?K0VQy-Pco$3Dro$VW_5%qszg{
zzo)y!0O`E)0#WnzU${6m2et4f7i0I-3R9FCucI!&>n`V^U({ZJrd7EW1*`GF?X-J@
z;hTA*pj@$0za%Y@fp~SOHcy>;9@5~OY;tCS)fY=T$`w2Bi>`Tl?UQ2p&sX?T8d1^h
zVu65bkS5;bo5yimoIy|a;};9!ZUM5>3WsHs?mqi2bLBx-HlR>>!(!;Zh5NV^yr2_t
zw1o{xpF3Gn^}cV>(i82X8|TeaNk~^f<(MUX%>L~y1-2<nn{CSei~O!n{h3W^x}lHV
z-BhW*&(oJDx6G`W8LF8s(NQuQ)1Ky+dt6&!vI8$`%(73O15$O|N*G3#>v)XNQ;oXF
zbE3<qxg3*DC_<^0hiQKp`>d8`i!v$p^J!t_slZ&dtS8dh`m@7JS55#M_$7ssxu|Qj
zVAUxhI!G~1PURyF@@=OWzFZrp)?k4;7BaktL)kXJderK|8BR+`U=1PhX}p5na;APC
z4V38lHkJ9Fg02VWEHhYsCA0CE7Q`7^T{+8{Y9}HL!RjgJRTQXEhE{xt1$kKUwAG;c
zi{2M@9~+`0KBlt3WvHihn9_JQ7WaBEa-nT1rFaE*n;0DDs2i{CcwL2Rg5yIwoNdCS
zg0|#Umvtl!TdOF3hHKD1p~u4%iDa=Yppm{2{~K}8ocVX|qg-U>Lc*~BbTdYkZ~th9
zhlnYCPDx9PyrR&h`t2&;j<l6ku_at?i&;{TxcBFCB_C?7dIJV9p^-w!6)eFm>+765
zrIpg3T`$*nrz?RlC&>=Xj7o|Qs7T(a{w|)z3|3YDzD9g14aeM9v@|u!B=<|<u;u5T
zKwg_ASKZ#51mxjIZ$Ov2*gNMH21avr#ZO(q8~aV0OmH*|8*nQpHya*t*GX%!I0l(u
z97|lfE#OW_ho{cdae#%K;uc;K`Q)grn#-A|?votKr5S}hcOU0Xxmv*T__&=rAa8Af
zl*{b?$~LogvqRT$e@3j%b~@gqFkh<aeZG3IZm*U)m5M=Ua9C2?sY+B;_RX}fEsOG7
z%1IqHx<?Wds!?HR@woR>2C=j1t#h%UetUn3o_!>LolZh8Dd2Jt0~lKa5LrusLw0wf
zTwLWcwWo@0`lxoiMYE+(-DsbIaMZ6#-$(18fU9o~1PjlA_Rlwl&)ALiObkqkaqE=j
zbKlPj-sY&ItEOagfZa+(Q$its4HEWY)PKcE$SB+FN~yXed)V}S^pXE;UD`bAl^A2M
zdD{>MKiRU^hC*wM5#!8@nyQn+!3(cI>wLLH^Kr5`&8#=jvGQ|%;uEZiCcm8+s|}mp
zCROJY*<$(@>CLEhpTI8GB^BAa@9MXHFl3}6P%deRK8W@9j8qBQeL<^CQ2ygd++TP*
z#kBT)Y^k-5JbZt{Jw&&a&G+`#Sz@KvJBVcU?Ma9TGuhpE$R@~QV%fabP|ob1uk6o&
z%<Q*fZ2z223oE;N!_h`0)e%PYwGoRVZ}G(9t?LrU@(&wZ3d?Dj5Q!8zk%u*m%bLYg
zqvw2l8+U60l*9Zv>3mO83LS9ExIF`bd?rsx$zgCsR`^|C1DT82u0Q*4n@1F(k5`!U
zLg;HjT18CYpDMa#?c`M1_5{=^orGJft<BUG%PW%De7BIneA38;!kOljiw+ewfq#+e
z(3j>^HTxPR`1!k@Njg+$)xyl_82`N~tW)GgtYdW$38gN)zE3SpB{BBay2*~j{s<*o
z=84e!`*5_#07N0<y3qyXpIOX^aU*W<pKG!n%e;2wj3(>jA#x8}bRxH3pZ3u%`QH$*
zSvKYx?e*2c;Z9e+_QsusGcaPnP}smO>55#M%zOQH9=;vcgBi}OwFsw*JC?O$cmpv1
zV(q;)i01cUai~yBGb<BNKXl2-$*N9%F_i}H&(*{&JRZD+3NM6184ziMVp0)SfI&mj
z?r6Z-dwFM#<4?$7QC_3wPPr5MJev5_tq)L{U+R!0(8%Gr8=4ThGqNxw-kxt;=hz-C
z{*>%-0*<QuCHLmJZ|Uf^O=`8iybI^zxS=T;5-G`x=w%}BL37g#NX~zE0-L|bH8jTb
zN$q~Io&@Al!*oOJ?6ci1$n<zK_Th;Q9R2U3;yAvB2biGImFw2YM)B{_f0dy07(fW?
zHE>;`=`>b;_3D+=x6PaUPOmja-T5A}zl5_-rnJhz=H#!1adxfIWImAFpfY^hk(hA}
ze?3ElU8e6xI0cxNEI>80!fQ`0E6y4)ATSgjXMG=&FD@=Z?YEjW7&T<-UgRsuq^dLW
zQTz<mWSR2h)$D0flxFq6*1UhzT46{L(6Rg2>T{&7>d!H0$6TAbHf!|N#KUL6NA4=V
zn)8@8h2822B$@OpE!#wsGc&X>FfbOwIFk!yLzF(X(4S=!%Odc_)pb`y(=K|g(Qz*)
zwMk-JejNl<*s4KS|2;Wc;%wr%{3-fL&C~45($x2(uGRy<B{icz+1dhG8T8>avk_kZ
zFKTQY)Rq%~{QYyH)uj*NJOzWazZ%*uS#Gy^+KaVb=>sC9>#XiHsQuWv6o1beK-EpI
zVn{35OQ1edUcpIXAkzN!I&#d42W)Ybz5V?yz$b45hsQStLc4yt=@<E!{}(&!CMex$
zQbtx~zJzJ{=rtoV1zeyH!nA<+V?a#0>8MS&q4#X2%975DlcjU$IEt-@Gj~bR0%d5z
znVCN=?lli<*X{8Q%dc&wS_9r-F&CIm+Sw+XI74kQH(g41!_L-X?b=|3!tUAZP8F2D
zVa&^Cqt_@Grjl=N&9`2V_>-te4^QK`GZYnzHCMw{12Bm(0>|(SQPSEP>^iZ*&i8Jg
z*ib+oW6sf!6xIucK%7lyi)~3uPoS;y2{dn;Rv8%?&45?R*^bDw&RBmV#EBUc3vG{w
z_SR`!_bA7*MH(~T_B`@x44|LjlaqhZCCdJ8D9d5L?)rSL*<A4UZXvDKed?!F#1Ifu
zD<)!)7w?b3IdcLY4KlvI?N=-h=ULDCW6h%43JMBN&JwR>Lr7gx5{4N;Q3^6BOgg7W
zO(I&4!iZbN5ylQA+aNQPciTK4t#rO#<zde?85sU)Q`>gCbt%43+bD3!V%BX`mX+Hc
zsy|$C8hagN$Cj<*FB&p(*26{8;}Ozjp@#l_-&pkPk3x=yqVfa|okhe#cXdwkLX%cb
zCyTcoz;118YfF_|0Nh{a(|m6D>^h6kv<k@vE~#FJ)<*GQG-w3%&2Zq_BN7n~Qi_O4
zAOp_k5M(-&36Fi^SO2@yf&ip)wBfHOlewZS0{xI)PTO_s@gR_tF011~yn?6{du?-N
z#VXV1DHM^9FAx%wsuLY7KTrzEI<hgnT$$CB9rxOWm-=Gnbk(TI$J?U*&F6w-x00*&
zxyiDP>UpANDzvO98O0*RY-={n%#@v8ir68Dxc-d&_PV2eAASc^nd7-EMW_T-9eGX>
z)jdiFV)^bj{s@<5ISrT8#}_zo>JlLPSaPg!Rlkz&QaEh0azT+s<meC(cwH-}6zXl&
zuXww^5O_8-9z(2exNU_)wO63&t;e-rP%pjEg>g(8`!e5bID<<IWy$c*PtQuPRT#?w
zT%&vZU1ZqWI_N<q_L5-!2awi%Otr07#WYkKqEA21J1W52Ab6R94TV3L5`Ujd_9)Ht
zl8D=89FM&b<EahIW_Y}94bsz*pQ<-Ap=SvGi}aCG{-$pHMTY_l@(QcI@iX|9BEj=v
zuFo5hJVStfPXi{fvLS{>pSzIgKAcmzB?rh%gL0Xz{qJM&)Z+yarz$@s+1?^$sl9G_
zy~3GKxf|-xJKvvDLn4mR%ym)6jnX0IWb>4IQ}w#Nk2T0#IEne6$1F8}6*RELX|1?x
zC<;HC>3Ge(*SXKF)BSlnH|9f&um8!vK2UC|2FiT1mP;}D*3n`=GsB6_y^z@nEADN!
zkLksGR)EvSIc5hM5BkVmJcQ5f87#M1N+|MuN@ZrvC=IHkOfBt_3$xs9cRZbK%W2T^
zCYumCnGraqA1u=kla({WtAF{x^5(hEmUKPuLj|+$hm^#0mwCdoiomN`Xri_`_TWyV
zdyC+;<A=%jgv5)K5QVCsyF0Umnex8wxE9!4SON~eI%HZavb?>CfS<(xU?><3uD$Bp
zw;zB^G?@SKJZ|Ngmx+k7qR08ffC2}tDe#9~+`kFQJOqzvS1k`VG{p!N4$ZL$(q<KY
z+*L};8STh8WNBFZPUp8F74Y?C0`_a<AA0w*X~GQt2+vwc6^0D^qpf0Dj<-U|`|Uh=
z?hn0}MpGjEQCciU`Q0thESHmG-ijW_jRtKVU7nl6ff#W)t;;Ri%<75vM+$7erZGe)
zX3Gtjtknsz;_Qt-ykaH)g~yyY*6`ST&~QLLw|`2)kkuecx_h7Tu=6G>xwQQh?RM6>
zV!{@N!puW-=LKv&1a|%mkj??mWac<u{-X~|@y$xnk~qbWl(u8&jtq!4_dVLfs1EtE
zgKnlDv?~yWXbaOl;uim$3eBMuvdPM(2sLY`m1gtcTQHm6t7r3LUH9-1(*BNekX7z>
zKgVZFY7+_Gyd!&%5I!BGy&eC#LocRe*3cw8N;I9DxhI%jGRmYmOUzNpS7}qR=yFf;
z`O$f0nlJc=8<^*`UK{%s4W$C#|Hs%cP*5zUr|x7{j+kGQqFvjek@o3*w>P;{Q}3D=
z6Ncuu8(zQr4SvL$vw+t(;e7dsSvKj3N*9Y8yeh;|v19W6P@7Ckyi*%1k4LWS?FXEM
z8p!55yL2-7s_7vb>G(g+T#d<@U0Y5{(zSfs7N;Ex+d@cE1Uoq}%bcXQQ?tLB&<e`u
z#DP*DK=jGJ&)2@Qj$BVWzE4s<m!(Ell?qyh?)hFIXG+G<G@#vYuQ6o8SBC!~#_=N|
z&?)LU_etb?e{ldlDlRhG{-REA2?>|sO#%lIviKR=>#Lg&kqm3b^4@~A38ILDgyaKV
zWiZCDF#S|}t@YgW_%lY<MA2_`=}GqZ|7bx!9W7DmiV+ab<zBN0IFsN-s`-+vM{Jy)
zpKL`0b)Tg*`?6Hp`@C)7nc<_NYBsH~T>11=q`)gd6pXgBz{7+JUE*vL%-6eS|FDe{
z#o1#a5*1w&0{**c#)xToKKj}EP*1PKLD6?<J-sV^Jw2+Ba+DMvzwR8LIVPB<rf#?Z
zdAtm2F!fETbmmI^TNi+p0%b_rS$)SyL%W=&Cf(>TsNwz&SUb))@&ug&)J5c&do^&c
z)s3vJ;}@RJMK<^8)CY?ZSTyE;@DQopBf@XzLYQV2s1&y_lI)k5r*SY_=eExj_bhr9
zTFaNvQtXJSI^($!nJZ(49>rzNmzsR$b5D~_!z!QGtMfhbpoyNO_#b!s=>y$a{aWt@
zx|_EGnvWAPrZcJ9k(OzyJqs!R>|XqJXMdhQeW}$X`sAflnXdEvy!n8^GZ1MYk_3~?
zl7uzg@|W7;GYUyT55faW7S-pS%D~Bs!RTL^Ld{$L2&7YX7cx)K?=Yzdl6|+Bst<45
z2QSHZb)s}%8+{S-c+s18BKj=((_qjx`dWmhEjMa%Erl(@d2y8<kyOpv+IkC+3Dq=S
zx538^a^_F^ax>t+K--(?Eaxo2A-kCWYo{wdAaSBGo8~SL-oC{cH*MYfinXH&@6Rht
zY3A3<t<_RUmITEeZKQR|{h#LSY<WpZmrAABWo6|gu22X6ocuWGuJ?33FuLESP&AW(
zZ?KDR-D}I2Me2e*%UNsX6ntGal6Ub9q2Bh|?s7I_fxA{pQFu!o>EW-%?(R>y<wv_R
z%XeqEjfufOS<^3xf7qnjth7qa&LRz2L$kNuJ8Sp?w97ePUYpo`GZx|-E9>Z3qwl)p
z>;YxPh_Ck6#p;C`stqoN$oeM+m2Zcj^PhN-ka>|{8-`7h3XR6H$D@Bg!gzI*D~z1*
z5^e=c+#*V+x9{H7PnBJf*H}cT_+E!*v2e4%MXF$F>L?NJd}q*e`J9xmvfl7&#rNw`
z{*}1<pp83d;%_z`drRqIG?p8QekPkj8cwcT#KK^JbG&WgWY9X(V}$3C))6k$o;A_$
zM+)k;$&^Qfqk+qw6ov(5_G*Dk5CF8l!k{&pU<k<Y)PWXKD#w?p2K3`2lm#@`y3(XW
zIwrhf9?7G4V_+hh06@<gvz-bGcMyoScKv<kYWh<U58>R}Jx+raAf37)kgOKl?#TEA
zL>VJK&QQBZTUDZUDjbp;B)4)E?46e3j8UZKfxb**@ngqs;ot+GqZ#49uJ&F7u&We@
zfNzwT<K)^T-0k|;oA1e3KeV=t5P0`AcI|_V?^Pgb7-9*FPWBlWf0I-ur;ws}=*5t*
zTLGsDf~1dWq@Ss<%Gdd%<k9JKV>M?rKn_CY;!hsDPy?vR0Y5^fhwPUG%@$`f2_dg<
zVbHj!afR6?{oM8Tl?NIDFDR%RL=igo(~M>vNtOw?1$lJR=n^msrP%RUJN(gya6E^5
zCs#RnaA{1;j#JccG{JCwo6)Gyu^=CSHby}Ax<h6l>TqFe1YpH#(Z+80I&0&r9o4Fb
zwC-!)-&xS7{$r$cHxs<IZ{i7otb0FuG+-Q1o*!m&Du9y4)or)h9GWM?ytihRku%>O
zPgVS4wsk=5q&Q8FQwp8`uW^0{^F^6iL`2kJGw^)@A|CQqEsZLkO;iw&FDWtkR{&T~
zbhCP#Jr2o3@OHHsXq#P*UcVi5QvHP7mp)fhH;~s`@SWj<5Yiybg+RbEC1}NM;@pfL
zF8+MWgdp1=`o;a7FWt0JnNX}wn3>aY(H(s90D_``0UlfCj(eQzT)NKLKsUyal6|XA
z+hw`^&=)kKe~tT7$yRt|92JA$R^Mo2#k%*PDVQP9?;OXIstW<st^__b6;HE{O@a<r
zvs2vlU;YYiO=t1X=uUZJ9rJaTY_H+DrikVz0`K$;4J}~Jkn;~wF`@^eG6c3xrO0Ak
z>ut^j5nvRnxB+Qkp8HpULfVCg5cP9sfLlI|W1VT{L~Bvv@_sb{$DN$mc8rHHA^j-6
zJ6tK!Vt`8(LYl&U%W_lp-3lzsRcbw6`C(-hYT^U6rmf9#zqa?<8NBz^iToVk=%I@l
z>5x>Q*W-G_6VKF`*Q6yt+ca^q!RbhUL+1i9{4Oc}0mxW)H%o11pQ$pa4Sis|B(U09
zBi86=*GZQb0l?O=I@&f&PRz`g6-dLKHrta;<8j9BqsB&Nm{o9}ys*Xj4WK;<pNw{G
z6Z;y?bL6d9xlNrSw;UPkn@j`*^Rh#8b(T@i{j^R#_!5ZgWSp0+k9tT5sS9<4&nCjR
zASNfKQG9(76t}2*tal}bB#QkC<s4_hx{{^wsi5f@0VGnTfI)Gv8iap9S+oM_yW4@j
z`=HOgl?f^!^9UDCo}+Y<p&YiJ&ghkJ3}lC9pJ`OIHOqkxQIvpPA{bs)7DzaOo-|pj
z`#s8Q07l}RZ28}wWr6Ms0}D(17LB91*eifpMn>$RbCsbg7G!~j8T;bZaEBl2AxS>?
z-o$?F-{D?DaCN%Q`}@0n&=X%O_!!&8BZuU`{zLbNu`s%mash$nim_WG|6PhKjbM^T
zJ$#B5zur;%vG3L|qr2}ARA3YIwM_1>4gr0FXkMXwZT$~GDVkf#)B&sQmlyLZnSu=P
zm&Z?74N*aCf)sfO<M&^qg+KCI1H=GfdBt2Vc@HZBwBz$PK-GZU%Wg9CiHhCxN`LO=
zfa@&H=z3sv)nzQcC5QBolp}%0t6#?C1y9}9Irun_oB?o<_MROVK<7t$E)%@nLSvi-
zqSsBU)IEfk6@M}KM7F{4akB@~6|I%e1jp4<F`CWAB!ew!x*wQpEO4zWg#nN#&0v(o
z2~81SkQUq-S`t_k;@N+d>O6OUZ0Gtf<Z+2ZfcFG=goeLEvLFIdi_SXKRaIceId=St
zb5RR-rd7pUhibED*Jm87-O12-r&~p^)U3sX(#zmhyuc^OSeW7SMgKC=db5CRnHr2(
za+*pDn*u&UED4pT88vu#`vcC!DfCoio>W6{8bh0kHP9BIA)5obXyN9>nvJb@c^Ds&
zVlH^g*0OlcF>SIzF{2b*JOeFh^T8<Fzo3344KW;7Cc&WpF+%cP-gfUiWF6Kg^*+Gb
zMY-I~w2gaSb>#*TNQO^;v_LsJw%*323WRg;Wx#in83MS{I1I+*zpk)dx2fDbq>IXh
zysQ&}vwAEi;IfPL6;K^mhrnO6U2q*(I7Th8W{X>2r<`6+agd=;Q=^U+KEp?P+P3>w
zIRWpR2uO3qD>_V_#EV4%ym*a_z=ROzi~$ho^U;AreW7>9CpEoqZD1pw;Ud!f39}rF
z{h4q68iAU+zeAwe?D?^dJt-<pSN^Dv`j}_MLyu&b8*gd21D?dPN|pKQkR(y$;fORJ
z_Ar(PMRrQFq-`XB-sWS@d_PCM`+3BVcOS_T5+W8Rj#av72YqYPbn8eGz^7>(HynXw
zcyB<!`G`~8S7c0$2=2LuWZlhKkyecgzFl6v`<TGmU%EJc`S^PGr@r_lLysb3t@K(!
zr18%{_2af{N2F35X;1|4p=i(X$jG0)4o9k2s$B<{TbG5mft6|k!m$zC^Hm+~?Xqh-
zla|+IDM%B|_<wHx&PsA_SY{;CaR~L=$kVu^f?s2P5<vKY@kvmM0oooN?H3r4ZB2Jv
z4x#>YZ+*G8Xwvh(5vnv3iiJDnUN*)tO3l}V#<(|;Iibxn;IrhC)MiT)%H~GaLjoxc
z2?<GJceFNz#<7s5xVLW-5G2q;2|4z;p7^XDtn~CyC^EDhOY!sg={Y{@ftMTtA}j*c
zpU)8KhK1ONe-a1hA`d1=B6md(H-Xctb!IXV!xp8%*j=E==>GzxbKQWSf1DGtd9x9v
zmSe^b2&1M8^|oiR(N7{kYrIvfB$+KfH<>vhcA~Werw<|KFj1W+!Gd_(R*<Vv;mFgx
zCLQR}eC{K({A);vlXXPF`P|h4>s#fo|FtH?CjF?m+W(${Sr~ookMVI9J-Y8<-pdd{
z9V=QQ%PqF74a_LGuaNoiR5evrx(q|6b9~krAyGyw#fVIp7(>&(kyJg($~Mf{kfZ0w
zGGI<3eA9^`iO*X-T4kCclFDnt+h=je#p;kAjEmonQNYidSsc;*#oJT2f%gIK=PLyE
z*&l_4QkA$411+3<JL7@Qyf~=8dSLU74+z&@IP`v8$bo%Y=uR`+YG&G`a^5Mi&#QX!
z<JPJgoh?x76P}Oy>X1$UiboD~D$DLS)y4)4Iz0i1<_hRA?JUPFk4cK|7oZO-#cG#u
z{Jh}IX;V)K-jky6!y3xg{j9Ijmk`(eA8;smC5~D{!25&A+`JF5v<_Ew!7ia#rT*06
zCUe~#%Q9HUmkz8(FAFQFzBQ+VeJTIhFQX?_AaQE`b2aqsg?@z4p3h#N3g5`U*Q!cZ
zyvQ^)K{=o2_0qG&tBikm-p7w0V*=#q_WD@}t`6q7u9l8{@Gn1krRlx&v{}(#SMTQ5
zR9z?L@qKz=_?iafx#UwmDqpO)pKtf}6yq<yL0_w}98-LyWoRf|Ags#aFvGE4=JZFM
z3bPe`sFyUvdp}iHPL8gJVmPeZIeq<;?Y{dD3bMxNI(twCZnEhRR~+Mocx9uO^KPz$
z)no;txl8cPcenJd@i>ReU4Pr{(;3r}PJ85uTA7RtuOz!-QPFR@-r#mzP@jVFBqC_f
zC&FI}vxpq|hJBCr)vmY1AX8|0Ioe)L?Rp%qH+iK@r`Hsed!b0(7Il|1U#@uDP_dGV
zgcRt|XCv&n$^{qNk!CC&;T0WbT*nwj?_AdHA8O_|VzfMoE4FY#1a<pw;B{wztRJk5
z-taQn53sG5^CmQC;Dy#?_J`FBVVbYvg?M`k^`1hXU-t=f7xMOxl#`k|#>@e^rN%0J
zBd>`BXpqAxQxU8}2#~<<@Q+}ZME$#_<mofn9K+YGVu6N@pK_wY8?Jl!*c$-ZgZpit
z3{J?tRV5-JuTpCefArqFDF<HsKj)Q(b|jWUM5CQwD69&3r(gWFbPiKP3lY|gj8QaA
zL>le1=v`y0!1#pDy{Y%c=8l+2@s07FU{5i;N`DSrPgwVtuVJCxZ%ze8--O+L3+)!J
z*ue=ocMxJipA{qas@!1f%2<z~3(~6$78+%AMCn-**0z03D#$kK7~btppc0N?9xBM2
zk<5&K`&M{E1}~(g1FB1_?F`p_{VUxtL10r8KS@+|t&hN^!*8N9G9y~N8x~BvN)&pw
zO{nX)w*c4>EM@((adN;r)<ib>ow?lWMImnsM#r5(EKiT^Qj-SR!r&IKAYCSw13j1L
zW3>-m&ssyxEJXr`4@!?o%`A2T)v`h%FY(4wj}c%wXTTjryAth~LLP309$(a3LN~~?
zrosI~@+aS*Tvlyc(jV)XQE0c-qW4zQSUozn_(Ld%g}5OjCGnx%?({|nrZsz--+A?c
z9B*LY-5>Pz@lpG^>Rwbrx_F0yF@SPW?$1^xlRc~=e1;Id;C^BH0Luh9DX9>jc6Oat
zW5+YXe|#N#a_w}EF@4@X_!LQj9YOYIW4+3Vi&Wnd7|THPV*cI$n}>frTLUAk9r&XV
z>-qiWUbA)Ov4dNQ+qK(_b>&Ls0!0U2$yRaV$(-`Kjz<+OtPsoEcDk?Ym|t9BtS~KG
z=?19g;qnw{(C>>726%h~^6Hi;Jc%>m9A*T9cw0Q_-}XT5lT`PcknQLk@<*v$pP-wN
z{aK-4LSGduU8X>HCUizIM>?3TUBOuW2KHYgF)&=aOOa;-DG{bP?HM@jT5Y!@_CtVr
zx0Us@o3#lBv}VYtUT@pl+D<Mm+7wU+nF-(<IK@5yp8AX|0c<-E>`?`xUi=_@NzuIg
z`l0ht$@eZkmEZFQu#+jCPcWXdgVllZh^FQcP@iF-%XIsm8h{zc)aZ(`DbzYy_Il&{
zcZ<3m`Met7fG}c*gd7*@H1k9L4DQ0h8=y|JL^xr<5Kijp>G;|3`eABjW-r=XPA*(i
z^q@5%DCPk6Y7dB?mJtI7n9nP~h!bd-Qjy(sAk?0qUswD+?IrHA@ys#;nx8*+3(}m%
zE<ihjoG$%SKy7{f5KtntEg%otd<j=(tPPvj%Ju$e;p@QNQOr*zW{))Q$)WdyDC};x
z-DFS7Oc)TD9yU-6=-Yv*QfrzGpsO4A<qN!!hd<3afVa3glrj;2@L{P5RYfOJifv
zv;=KrIR23+p{qYdgfiTI^@#3$p}!j1A8l`^D}880o=&6dC}C^x0kKHs1o_L#akBEY
zN4{bYmJZvJYWWitd|#-NfyggvBfk#+<*u4hez_+g<ff;{w}nH3e!mPcPq+>czx{h~
zkZ^V7vUG3N;7J!`hCz_pK^zY0*v`Wa?VRsMLAxacAdobR`%gsLK-*4~Xq0X~gVo&3
zdcMuN6z1pR{U|Ga)P)uC1^zUo$=o~r%S3a%M#=nq-jHDZUt4b4OG3TK2n-(rXg9}7
za}RTyH`1893Eytmy=5I=Gh3AIMyR;GKO)3WK527k+*yKY6?%DsU|webJ{53XhTw3%
zt~_k>oIPE)ub{s1JLIBu?QUHWdD;*eN^ZNL`3clAF49rOrLRtt+aKNuUluFv$}AB;
zbx2e50#V8>ARQOO=5reE@%n|h0PPn`><{5FM)Jb&A1r~=NL|X{T~N$o0P>e9*+3EM
zC5~5;l^3uNlgielA0vJuIDZd1;OI{4)BYi=>s=M#kzFD6>iek=Qr%tN01vq>hlxTT
zU!B3mY!}|qcrMISJznv+7FR+*(-S&UnpJ>C`FdKm8Sm0;OGuyIGIl_VOD_)MAj}Cx
z|6;fsxu5YQ&y3ekARlun4NpNW&^gcxx?%2oX9HAqRyYZ$eky)I#_m`1%A?((&>p1Q
zJT*JJuQFtVfKJE$KI|Bmg8Di;0QCApx#A~VFVSQ#e?Y|-#~1~;Q0O5oiRGu)R*+<!
z>MvDUJhaTipO%U|8EfflOT%|<J0t)WN?n*!kog0iveSFqsQEXtNDc%JscuFKLW0{F
zm;wa50YVo*ObK9P!XWn=mk5ht-T*cv339AIB@BVdp->_6P!ODcc-IJ%{!Tz&pJch2
zHnKD~3}AV06&qv5r73s5z#Om@_pFTDd#vilr(A=gsYb|S{rKL@##t8Xhg#jFDx4}5
z6{_<*Y;k3;i;r3Eg<PHb-LR>0m1+qwPD0T2Ws0l#Iz%-F78>f1^9%%t$^G}n>-1pK
z!T;=ql0A@)nrM4kbD8-j1^1&5Y6e!j+UzUgdx%T)b`K&v4obw2N%^pt#USJ_eRi~4
zW~6XB?M9FL$=T-b%~oiw`16FgWI}=p2Vkuj&lS(Idc*O5+56t>34J?-s3bQe17@PM
zQ!K6czy(w<^tK{oJ|hzcEn4{#8`?QN3Ud%1ddOu9ytP=hK1_x&`*hG({C_XR<mH?A
z&z#Y!>SaAs+dgxDoks(te#}T_sD23W-mp+sNta|wmwXe)uHg<Qtr8+Cy$`L=9*=#E
zDqnd44zX3*G0^uLkQb{);Pv{Dv|Xfzu%IjQiFcxG9n+4*!qipVWc0XevGTOej=k&K
zcQr&#nukMs-{021AAYqzgdYHr9FMklMn+l8diBHdCT{T_=Oa9IU9tO$gsZp6my^1D
z-!S6@lskX0kK}y=Qp35Thp%>v&}FRSy#wmjehv<r#?yJE_0GNu%gDuok<^rcKHRtt
zaxGN`{GY=2U*F)m)4?Il^mKR-;v2f<Jd8a*M7xCCRzBZV&OfD@VME2jn<j8BPOv#&
zC33(#suI>qvL&94Pq9SuO3q78!<TSFk+Axc%0c=6I>K2Xj#D88=Gr>VF6Ayfpnr5d
z1f=6C67Mm5Z@RH1>ERI&a!XHk_<=#sgd+!pEXEZCqP}ux==&6zU~%k`TvSM7nweyh
zQMk0BvPkhxpsVVfRxnriKsFW1pMT$DhI@HKd%@fzigTk@ilJ~KD#(ogI4{P1%yT){
zd%wFGXJ)+dYTi1(xL8JslxD8WO+a&ElJ-e-?1o-a9^sufyf}r`CE+17zo0k)=ev+_
zcSSSr25D38FkAFN*iSs+aI7XtZqpiCgFsF5GS4oBNf*1}&{*|iG(+HA{O6Aj`o4p?
znxC}r6nv5s1Wo0L6x3+OWM~wBA=hFp=E+2~k9uXBC$J<)Wf-{l%{20c$Xub83OYCO
zlj#yo9ec>fmpVH*h0*=~sp?8^wHX&Kk-FMKcQ-(o%ZHRzx>o(vmJ!66F7sArUzNPN
z<T<eD@!ZHTcvoNO{4n+>{6=O>^V#i3_XiMOc(Mb;5K~{XFStRhKEs$Q##;-u8;=1|
zX{Pk7OEX=bPWjcIM1bl1)|+kjTljg@_#z}^_-(Blv+NORlzcFpK|D8Q)vWPNQqO}^
zqeEbUr|lHY9Vn(=wjjIt-F4x&kpq5L=GC%=(N_V|GFSfz6y`jT!`pq?#sJE>)5uf6
zo73hCrg$|8BSBD11k|xAaHehqWvkINHWx&|$&;kHsj1f2=Vp^mt$+0-`|cX!Y`S_S
z{08Rb4TCsa?CLm+M-VvP&7Y5UBz&IwE3JaX3Vn}gi+#stX@c?!eWgfScR2ZSoT~Uk
z2VBQa25YSA2UvDC{xr1>yeCnjg^~9eXL<%-c<*`(II6}0X<v<KVXu9?l{g5~V)=gg
zT2iY)5q8_4qm*d5!3!39Hb>MX;q~$%D~QfHVEG1l|3-QP0oqLn@CFnZG?gTRS7F8W
z0#I1T#i2d)WGv=>xCiR{D8&;y%ps|^Z^8dOjS6?V$slW1dv2wN>E}uJ3@p=alO>Cv
z5>*nE&ph$@*C}AdUPhruQ10=)s>y;hjIcIv83`L{1)E8L4503dF|Ac;XsR~^y&YZ@
z;?gc};Q}!^&5dz3K;LyH+~4-k53z;NT)H#husws_UFAmxo2##_!>je@$E{@X^IcuF
z)1|CVVFJ{re|=PC^bWJAV*R{6FgskfFZ8qw_-ndrUsPWNL+=Y3`}9Jt)sN9Ke^jBu
z22t2r$bl$6U-NRX7I_I|L~9<FIg7RmJFZf>lRQYCD5d2NFly9Mw1Jh+4{j`-x{rxo
z;5#I`w(c9~t`(J)y;h5N>Xe56k>D>+7T?oRzGw7whF@+BlBwx4V86W|i##k9fY1G4
zYh^WIp&SpD8`P+olS29)2WBkH*&X0eXt<~VoQ2_v1%hUV*L&iaL<9t(it9blKA3$F
zA2V=2z>P>eh^4)FA(SX1A*N0tJrf9b;&a`+GB`dSlPLv-p&TuISHukyr1d6Zk_4Dy
zcm$pY%KOEN4J-KZY2>s34Sjzp26}?xs4GnI0({8kB!n|`1me76wtG-(%$SG+z(Xdl
zq(oNfbMS(~i{OnwvWM(tIx||NknVZwS9l%WX14S=kB^V@@<>_gW%ZFwD8Ff@beO%8
zW!S}`X&%AM=g7W0zM^RAFDb2-piLrmc0>vtIDMzC3m95;?bbig;&6*AT?RW4@j@w5
z_dJLbm<2P18qDyG@wambk)<*-QY8@3qZFCCPnLuDpwQ6JJg@}a90EFg!Jr1M6O#ZT
z0H<?yI3TLaaJz|UR2hH${BlFZ)hZANX^KEzUX7QcloLV_Kur79)x~8GsRm-;<g$W9
zfpU6zdFc_f0j#Sgx3OpT+JoGnvc!gyMGd<C!b<*L-9oZf>2SnKK&5ZRv@xki*t*&v
zdv45B@<;0D&5XLF%U)~&8H@++v6udF7t@XR$x(7r(l?5r7@I(35Oys6JeB_Z>~13|
zC4Rfw6^a1Mis-9Z$w3<dR3W$KgJAdVJDlLIPB7ummln_+`afe^Ghm<my?{8>swoNO
z8}xNmkWs<@B?&1PX%Q4N>0QjW_c>3~EqX^1@L6DqmVbaP0cE&>J`b#~b{Lrt-RK16
z|D07Ba%%BLzYawI4JM=8u%o#i;dOHQ_ms?6RVJTVXctLA)iSS!J1jR2gDlp&bMAoE
zAqa&`zXv<~E!$}wJA^%jX9<j)S37J-3Q<}xWXwnt>>=W)PNhMpksg;e5dk?-=G~zL
zl1Iu7YgXmso%RsP(fv1X3x;qaF**4li+&ad$<OEQNQEY}Ls7$EgI*rIeG$yUL%Y(&
zHBu;i$p9)1$J!37$WI-ar1$KBr`6v$Q-xQ(Q9Hz5Y*W3sIC<L{q(g%93$RL+nDAau
zVYAzI9Y=ySXEr8t^vKqKf7)-uUHsOd_b<v!xJmTm%{PL-<f~@(;?>xYZ5#lV>1&J!
zvj$t?K#?FW8oY~y^HP94TI~2QK!50fm1$YMdq|f*S}uvQ{P-{1)QOnk7c@}f7Z8UD
z1><bg_3O?;WOMW_Xh7ivXo)IiHhG5=BCR}oc%V*v{_760j(*`Qjz>KT!CAjqdU{D>
zb0P#P#Ud1CVkC+=1aNSr#R6$D3+RBWX&QHQm1xSFbu3i)5#{L*-)Ol~o*fn)`mNS*
ziD)wyyw6QX0;Q?H<G+*;loVUOZo5vc3WlMT%8lT#hqn;^>+rO1^jz`_3mIkz>s0y5
zcm@_jL78;*3Rh?<^ez93E%fl;CZi)@WT%2Bfv+NX0@V7%DV^r$p%RQ8EKE4sQ*4~*
zduaR!jnzC9b4cLH5FMZbSb_}79E~p%at9>{<Tc3!SAdA9ZpTusRXn$Ol^<q=DdH5S
z4+WyB0W3Y8GB^2)x5o86osFZQXe-h3=_^8#_x>cDfU}4qixPB_3z~2!o4jPFe@E(u
zTR+(#9sWJ-Gm=Z`_g=#qMDBKPgwKS#t&>EKv8UI>Tr~a&+lxqrjQJVs8gd<}KqIy{
zehI#0ZWNK9Jeh5xO<-XfQ(k7KLA!mIsaycDK4!IagYEtRdltS^J%oG@DL&EJVEu{A
zL(V!DF&_-6@rJ891p>|}g3RzC(Z%d#3lLe>)t0np-1gKKWe*yUKr7Hm(T>211;Q|T
zC<l<M7q9V9G&D#V!=I?@HSDJ!E1)IRNov7~IeLm8AT;^*tEk7t=6WVCMwiff4BLTT
zSa2fVT1Qy-09pdMoLHA7)NI#>7dWpL$t=uyG2FjYzc={h0iXBICzg#~l^%cC+8;C2
zG=g=Nm>xZ8(ob-`Z2*D0(es`A)^|d+0x<*eITL7p2Y6?=W86K<wg_#kI5x<Y62kyL
z!W?9exQRK%q5A@AQk9&Uxsz0!|B6CTEL5CeG|(^V7J)$M2GrBpa6ZFoe$@fX&k)v%
zZcK<f)^K$;Vh0-2c9`*+I6Zet!zbVfyX(jS?VniSunbWH9Es0<(V>PFq*uliFoA~n
zqzq5qLvc(H*$Hfpbvii`&;+p);02k^zL?VEgwidGrkeC17pTz7tw%hP<WNdVyNH3i
zSNt;~;wk1{ITq@{+m3)UlT<MZ^tBJmRnZf04|VItcCh3Fx;r{lWTD;8gTsC+FG_<p
zcmm_3Uk0l1M09kTMdcEJ0vqy{f?}9oWSKJ4BGJ5Lwff3q+=meSD%T!h0DeqUnBw4$
zcp({Aq@RZ_>B+ulF@iuDG%WbvuMxs;e<W!2(_)Exm4Y91R+El{DQ-aujy$IL04nqv
zft^<sCOMsRA-W{nj~vKhgo12&3N!f5Z&W9Zy|`<?5h^~e-J&l40#g+j|NZQXAIf{g
z-t=rizEv^g_{}vH5X8<QuLOMbi;Ko?n3s}le_y$s8MB}=OCV_t14?o*2=GCeoFk%$
zK9D>x&#tUXkrqReV^t<>`5GzJy%GJtp96x2z(nGN%(mYkA>J}D+GCIkMobms1sN4Z
zkUST`&lDc}Om_)RIL*?KRGdT<aSOaC5IsS*%;qNzD#~!Y_loeK>RUyGe4h9jbcDXK
zBFb<ccz6WBfC2w6jz_f2Kcpfq?xoOJFec~Yf6onmVut!n8Vh_9_%%-We_u2R2OUg=
zFfdM@zd6uTu<VhKLB`rGV~E=}JRe87dm4!wVo;Oh-U{Bt{kPVev`N{#Voh8@ZIo;Q
z@dh2=SWUvAE$~*8SO27BR+`wDSRGvD7tmvh>q8PmKpa52=s_Ncy__WByhb{e2%h56
zlzWzR#OYQw9AVKs{hMgPRAEt$nv#QEl`mJ+^E3Z#Bma>t_eO+DlC-75@F_3rY-i2X
zYq=WDrpOUD(!}LiMhYZp12M)_Gsge^Lda?O3Rw(kvlEUcW!4JY^mYzyPRC#IVLk_F
zZF&7jUcFi6j$slHb6aca5>3sb$51EkI$4czf+a*(b-C<TwP5IosA(BrsXqj_AJp#3
zb2*8US?N`^ObxmftDW{5cCFagyGSU@g-_9H=b~o6wd=+02SHQXGV1r=j_edCvN}h8
z6#Di%KMHY>_`el;jfN&`Tx~_rD)cz{N7;(hUjjOp=*x+ak7B!mad;#Vil&UU+QTKi
zu?3HENgr;-cgoy@CMODYX}Oh@pOsO2MEU%$$xVp=PH?o>)`?=NOjTfMWYBU_7zEzD
zU8)vtgQix{Vj;0EizrJdLF#|}j$jaH*w2PGgK<zw>`N+MfcS6Ye<ngyQ{gH$+}*WL
z@W>Kommz0w!kVLhoF;jhxTQPa2Up+7TYMN-)rRMcPlUr>lYYZTU^;1;m?2|dQ|$Fu
zg_LMI@uga;eiajk^WFej<!)ST@jfP}JLDcm9CY}Y-^m%jcNqKBcc*Q#(xYMQL@9`w
z+((zFsdO2{%R_IxP5Y%-$1|VrRi2c)jhp7^dSZ(3*WV`cEd_Ft1win5^S`&ZQ~YKm
zuhw1NyG1=w!q^GA^<le0OdFoL{JnTOcr3@45z58YwUU7zv-3EUVIEdx=+ez^uJc{X
zYx%9xPIoWTNkZIN4S&;6sm)SQzx}t*<L*Cj74sLPsDI5Yw5E&g_zOQAEnZ$dlQ2%1
zarm;x4yU}ir<V`e5qx@b7@pO6bTs_sYgmoLD!-ow$|btqebOq;s{6!sBaKdNc2=kY
zBaWxvSP88R`^$LJH<dU?2_Oe7JV>Cgq*ny*HAbKhA$Gg_CQhNS`=5u-)fCWVDGV3r
z=dB;D2@Xu*)&Gqe4Ru{LsUGbdG_rbg-<bb<t(era(P-Ggtt|J|?nY)bK#9e$o>U^S
zAjc9C0$%-p4k@O%bRVQ({r4{h3I1&~$iF3oB$eQ|Lh%cFAMGXht;6zCeN1K0AvKXW
zV%1xUIPl?c{~h!goWEAwcx&BwrPQxV`-mJIWJ@&lh0?WZG~tJpVUn%aJ>HB=mf=i)
z|IS~`RDxxl@lM?0)xW(I3NuG1!J2G=9HS#DnrDKFf{UX03ANTW?6k&My#xX?SfooE
zM6y&AYm@}^xvxciygr|wBKa;iLf2G4nRhn%#TuT5cZP2J{bkTF?|(5H$t)N1r%n^S
zs0EadRnZ$)??hFSS265YRQve?o5|dALzVx{?Cg_Oi|gL?<^2+@{NBdV>258dK@u1=
z3tQiU8TanLxJD~V5tsK?bniQrinFqs>EAlZUa#_((`;hS`8UH$U#Z(@DdrIWtpPO+
zy~InNhnGF{>P|JoNvPP3SF(-?jEfXWKE+iyDpHFTGSXM#|JF_h2|2dM|9^Mf@A;!h
zW}uQVZEq8)dRbA&kafCqS&^v8#5wT>Y4H8OLqTDfgDIo$|5YEdiMA0n8(x2Dme6a^
zsFhCh^-3ryq$v6r_HNPrP<AQeV*yeHAxwgvM=a&sTY!YfLSF3q7f>qzLp3cR$M-D@
z=KB><tjpiEk&DJ2UBY#k_8>Mqa}*0-cwX`-+oA4hJClx<Y%O`bL@p$IR#v+d4oVu_
ze=iGsR7`{zHp72W{{Mg5AfD`XT9*iU@9q<k79QwD2By)s9?3fDE9#F%D@hKh|GVC$
zed1ZfsibLder{0+dv!Sv^O78`R!p&^n{k|4=LP{G75q}_qLsSV6}G1*myi|iCoi`*
zxp86z2>;!{wJ+&zf(7#0S#f&{w7y+>Al6%px|HoENWTj&+sCvrAo6@uHmHJ|7p^A|
zJNm6Cz#;-QTffQ$X{PiClg&n0Iz_rb-dpY028`|IjOx<frgazg#Iw|t`_1oO%?^B%
zj6J#>tnJs`MZTjMuh{!aDE_~Xy&U5yo-58)JIluXlY>Khys}AU?{G-mHaX(5jvfog
zx~V)q>Z$28b8p$AI6nL5GW<jl|M--yIlYH$r`oI3JMY976<S6i(4lp5l#_F?y~E1j
zBA71tbQDuj0QUSa?jno%Sc?YErGU2cXq!8k%HPDga*ASItH%>w&AZ?dMBGc=Zs`i%
zDeh2_wXm}wGx&e&aTb`0`Kr?lu7AK|x8mcua1#XJ6)#_ReB)jcljJ;86sj5rfTt<e
z_hN*M=G6%8D(}KC-r>Kk4b-SD55}C1eYswm5#;#blXHr@h(^(-M~g}?iX>qWwy@~?
z?KH7Ha-=AF;sVk!NV|lWEvc1V?7*eq`tP#8?UYB1G_DSyl2p9op*{AF`kp}H{Fp65
zT`s)O?=bX*J8@6zdSX8KEguel391RJx@D3Pw5_tz_j(^XE*r|Ll_}j=@9Lh^$>Q>{
zg5ayGqGs5=Vb8lf4Rh9PCXxTmo!rG)5hKYBKcO;i(OfR?%(E`M3DA(83w^UOITYWd
z1yhG&`8_O$@mW+!evXiU_VZ!lHhmK5+GnaK9_N%}PAhB%qLf9FPeoEtg@eo7?0YH(
z48rFzF~tFpSQqnP(iAMoZq-?(`3l?>q`?j;(O9H0%9NcyTq2$FIHPkrCLEp1$PgWM
zBU-hcZf@xDXomzCb3M=%MCVxs4aZI$QflJQq>}|aKD+jRYm8->%2f^+TxL&332h%q
zDSbPj{x1MXk8<$}(nmX|zr=lofRGu3mQUqD7E$t;!5B&N+Hko=J<Aj+GL(pzkn<%I
zJO-ZvE3dcy2V$TOkgj@Vg%JFX13aOY2lTXRaUH^2>4-~c+}P18KXslNqJSLD59@G$
zexL@GQ1d@0Q}=vBtV|Rm+ulsEFe|9ern}Sn7cp>33&j}9?`(pFGPs*jWHn01T1W-G
z6!LZEZ#IwIKF(@8M*Dfgq7w~o9bCQ~;x@pKFV2yE6JWY63}Q)Kyv?5$G1hi~|6Ol~
zZ0=)}`n6;8-Taw8o7y>2M;IHFG4jlks3&D^e^^taOlFG>54p7ZJSgSM@cGok{h&ah
zItw**$B43jZ^Lyn^jBtrUhK&+o=h)8|1TrfpK5r?tTQ_Wa2^dL!_^NQ0c1|0l*Ki3
zW*V-*2={mb<~VS|8z-W^2egF@VJ7jg(Q~Q|7`vTx<doliGxzq!--jOj?-tmE`x58y
zbGdzOk?3Njn78fRG+v$4f&e+OhnzBFWIw+xoK}tczpNXiw1Y27lPV{D3-5TZvz28K
zQ$79*yZ>@oFwFn|rnUd~#-#Rj(2FP68?1ZSCIOkPjN@suRqtD2o#X-3|6cTy>I9|t
z>5*Pds{SvwK|}5T`vb#lElX~E+)@W^jX6>giVQ=tF66OKm6fC2J<Hn>J6dcVT16;@
zIfCEve`{KBY}r1+M>$+om+3IUSbXRUy?rdC%_1z9gENShF8FtMQ(voesjFEh!uI!>
zG20l8xTS|B2`|VlXeZ9G*?Mpwrld_l^3YI}u&HUMl5yVJRa~Y#lW<&U7F2A!i~<?6
zj3&F`p=GsLqt@>?{IRs>ZHi+tv#@#2*(3DW8U>HeN#BcxN|8#PiOdO)ijqW|HMkwj
z=L#xbwj;lnZEt?}eMO}EcG$__V`ai8v017(EcIYYuy~)-S5u6<Cm&>o!YCFwG+BIb
zt?2}3R1Ab>u<Fq_ey{4*->4(q^;7TO{*E!q7+oWEAMD{oyfesLGW5Y7dTA~+$SHSo
zuk=p1u#t85bv!e+>ZeaY#7tb8RGG;@RuyF`wY37NDcLj4n{#{yzah?9bZ<Cw7i1aE
zuaN{JspU0%urJerK+A~5&l_DDi<s|Cs$Sc6k|~?gs(ZKTVNE?A60{KX1d-y_AMZdO
zJ4SR2mJ92J-o@3>kH8<BJo572^;$3sqzBatK&CKkZKd;@!l2-sNb;%n^@pHK+_R5M
zSaW~uk4oZtiD43QS(877sHbs!8}|zuF_4<%j6FNKx8_V3lb^{qPOQDtjvm<j?``c&
z#A~x4u~w^m=S(A^p}&Kl&to%mEGkOw+w-dxkdKO_VC?C&lks$^<H`Etq!wqCA%f6L
zKL!~!TTraSkDIA$?K|!8m2(|nLn|5nFNP$iIHF5D-QQ^<B}V=APS}DgMA$SEAmxhk
zoRuw|Cz%uYKpg1g>l%*K-X5!@VCMZgOH$~^wj?=IJyyic3i0i0DlWt1JqZTlV6ABB
zFDh(Ekh5N6LpcAvUaC%kU)Wle(KLCnwKo*iH6labWAG2##`@(KZ{Kmj=j8dQrBD&~
z0|BMf9VNmqXsvBo^WU!yRnl($-2BB;`0!_mXwB~G$%WWv|2(MQ$kWT_vb;aa;JZ(Q
z9IJr4JgbWN=RwLxqV)y?@BO_h)8}`=sqOcjD<Uojt8(a?&Rt>sRfN8GG^Pj*<Rc$R
zlHDU#I*%(|9uC>by6?&##quJv$V~dQ?)x7m_y~R*BqPcwf}A%o3{p+VaN8`CC$BjE
zT)eFkxIBI5s-O34vF_%u`@S^`LxMjI#0)LND|!1rUb$1gkFpFRTW+`J-Dv!%&d`gW
z?C$zgD`D6JKPC^cOCd`8{7ZeMM@<<BjX9xdQgopl3qqwW&pnEdhqOHJ3~zlAybYor
zF3UeJ$&@-F?!ju;33BzP`Xj?Lp#E3epm4G%_mP2ca{50N^1o1m|Nkabc^&L{SaK~<
zV%)0~S$3?>NB~XvSJ4D*GI~S?vz|6l!+=a4;lGj)@ucH7bB4C|u+<y$rL^Pv$?7WE
z>ZsCxkwTLFSiq;fc(Bk=c_aeF8k})Rta)sbuGA{5i2d*y6z!P*5{J*BezoYegry82
zAekEF#0dKz)i#Z!Zj$MjACA>?Ec6vJE?k2mLGrEfU5+~KgTH_#Z$BQ>!ve=}vQran
ztn*$|6_bD^Z<cKAhmYlP+3rGC2G)l5kT|p<*b3K6W`N6o)Fc+$bR~-vU5;(VnU&Kw
z1o<BSfhnIuKkjOq%AP?4yD+4qlWLjr^6m8pDvRtB_P}v+zyu@<3b{X;pkk$&?N?ne
z#~$r((9Z9aV4~73WuLs{D`$ya^U^F!V9c-mYfb$oJV4yP4sZB1cEtkulX62@G=clX
zShjPd1Tro~kQ-2>0@r;@PQ%<m>~)>&F!{TG`MgYSV(j4MQ(@-Vzno9mGBa5e`>*ZQ
zps3!1xE)k+x4TEFniTmQJt{>|-hXj}Pga8aDy?~&IPgmLH!A%i1xFyx481=QLz;B{
z<b76-m!y<Vz&gf-JbNs3N{GL;k~+@A7x%=v|5My|M>X|z+oB>(1Ox%8KYEqkI|2fM
zPy;Ac5Rl$`S44VEK$?I^Z$arD6{QymJv5OfAiYTu-;Vsg`+fJ_GRA%Hjq&)4k(`{9
zvvc-dbImo^+}F$U$5I_{gCZU3Po9-X%fwRQpfQXV{$=-+pAKZx!xFe^v7qCM>hER2
zSSSO`WCvgurw7gdHq55JYbTVcCG%_=Wqu$+tE~oid988!I0EcrOG(zT0p{^;9xa%`
zRF_X4V`+--oO3My&!&Rm3>))7kGK6#{i*qTrmp7QyjNm>NVQrU^K)_4-0D^TO7$D-
zEYE|9^ob6ZU68t8wkP)sj~AxE8#d7Uig=7Ld5~GyV$y+f+?D%jEw46Zwj1GEKw(dP
zNi|`n-W}RMlomM3itv(T`V&qsGsb_5q2M)e=zqvM0l#DUFVxb%q#u7$D9qX<@Gmj?
z|J_NWJ8!Q9n^oJ5ur;n^I4m7ayQD5GSO_J9uZc;dRwJ3?6M<)t5!jxHRER}{K>5SZ
znN?YKu~|=qpAzN}1w6>7#RNHCj3hjv1;)>&+YVZ-2W0+Engm51C(XACa901&ZkQ2K
z{*b0x@$j9(P{-Sbg>V`aHQ<&pS7Z6B5o`=?smz1fX{~0(;&yqc8%8q)3g83GmuDl$
z6dIC-ZMFXn-scFq{+vzs&j&F{+J7-hxbboqzMNcKy;C<&*ufZ5JD%Y)vIx1t>#x}s
zhq$gTuIOy-p2WnbCI^qJ7L9_SX+>}J^SLET-+>+~(yfT-Zfc__;eTEjVP=P%;&Ax3
zEtPVT{;4(~)4^&nL)OksdU)ATmx*j5vg4v<<WNe>A!twc2}}>^yj{U}k9N*3)uT-6
zBkDoBtlJDa1tsQmv*RGNE@Wf80$li=?J(1Eot#ck`sff<lIEhdhg~Y_y?@9=l(6-I
zjOL=u0qUhMM2Or8ixGy!?uY2ton^E*wD{Q7gWl5%k7NZ&^uTO+QN1CWd!jMqlhE$n
zMm_qe&{V<9Qg@Amxy~+3s)#g<l3*iRJu*z`<3YZ;KdxV=?s34xQj`|$Eek9?3Ut;z
z?%8Me9K|_tbMhCPCw_WuQ`l&jA%N`yA;%MnSc<WiWPKdg6w!san`J5OA0k;$J%v}f
z7R%u`4Yf@5el>XiVVr+BwUCKZCnuOW_9@!M`hmPBrY^I2#mb{RU2cBifPFlm@3y>c
ze40^v?aAvX<7~@b)8$&ze4bL%*Ari)Rh$M|-G&~^wIg}i5;*E@lQ;`LH!153=%ZGQ
z=;wOp4^Oh*%K0Q|?8NoJ0%d7kx#Xnccj-4qu;Iy4x=@biQFibZiI@^wJ2JbW3TXt(
z<n)cQi{`Q6pF3~uhp0CSu5MTGnAT+7^Y1#EFfT>NLRus0cJMcA?}rMTZDpM|E>2U2
zm3XVm_*9P>O!dhx_-ho2j%`li_mJrEM_!?pEqnLj$?vP!rv-x2Y?e)BH*Q+j3xD%<
zgkl^xZac|meO!dR<h|F@yC{F{<8$4ybp*Y~{9oS%Xr`1_agsfq!xFClBK>kz_eD+l
zMxy<}y2MRmmF*(r7$d4_oQo{=tQe=cq<Q!|<0t&t4ShB4AdG$J!3Niv(-Q*xJx4yD
zS@nmUvdo>*^xJWci94Tc-VOf#mV25URh*!2+U}WlOk_~o7i43)mMd-YXRIQ55Uwii
zqid3sIYr|Qr?<31_eNMEwTyCTRSmY{tw|mY!Iup!DDlb%Ab6^n$K*tN&XjcXZG6H5
z<AVbww^9m&Qr(9+-)$JDAa73LPozRD0^~~#*r?iE9Ye@rV^2)RM5B)Rk7VCn4j~$g
zZ%8J$;$1Me{>$NQxIPZSgo9*l$#Ijk6k|GA>;>dIBEoynKNeTUjuy5-uBL!m7n0R(
z8&3?w`H25#@N`-pQNNk0+BWu;o_N4Hw&JXymtHO~RDy|y7jaLXZ@TA1Q>;B7cuVk{
zpOWBaaWcWa;w=vz%LhpeLr3n!#Hz*i1*gz-JrE9g?%rYMuB{~_VBYfacwMh@^`fr0
zN8f28?LdS_h=>-Vd*M8^9R^F6f9ul=^BnDrDk;|@V&V4DvQE4^6pcpi`V6CACAWob
zZ~DH1uZ>!WkJGYUnAF~1B>P-qQeI8~34945N_^XvKQTxgW-rqs#k;w!S4%Z-uk~97
zI%$^4qPDo4E?wZ};LzDenin-pbgSM$vaW==XPQBQ4b%tWIKlXm!TB?o`ySlzdgUrn
zj~+kExkbB0ww6pi|FmX7d}cu-YB+ztr507NJ;E{jQ`M@2#0Nd(0zYyQPC4YvW?9~9
zB!n;ZY~Y2f0DF5K>VOCl#wfS4ygFA~<ON@RLUiN8k+YV5W>U#9KV8erH0;6zoe`U|
zd>i~CqF|2S-i26u$@@d^UH7gFuP6ee-h=XMjVD7$h>@FYJ=ehrd44geJnoY~oc>@`
zJ+CI(+Jc;HRb=S<VNCq%N%>0het*)#^Mx7PKp<#5h(8S$iIn?7pJn`J)2Gt?o!C+6
z^uE-B9^0K<w_cb+i7(d%?CMPKER4#x8!>T<aA>qQBYxd&BpGrmDel*pZn|MymKC|P
zm$2yh%F)xiz8*-ouj7L(kHJjkn?+m%q`hqwg(P?O2^Ad7xH)Nbd(RJg<EKrywH8`t
zo}x!<V!|dtmZAIUE$e2bkiY)MsgzX~eSp`E1a?Eo$_Y27IFAYoTjT8=p!*?=Crnq-
z>{2gSQ<<k~9}=2R`fY1wFS_KVt7pIxBIn)cS&Y+SDuUi!`+bp~;=bp&;ZH8<+@aFB
zAM2pe@SCxyggC?EIDWB9Qge7Mu0j*L?vQBcjm}@iq9O9@o@n|Kz0ksY6`t42IwN{4
z+RtKeRvmml_y>Dcu-V1PwhO&$PpPa`lesDzN93nASuLu6*pmje3l7PfWY>{=*=$Vp
z>0A_1vbY8tuF{lD*0#!dPAWIVgjUbs(9Dcjikx78v;7&Szkd6N1+Fz!55Q*7>DW;H
z%v<N*Smrt=cM6|zMGQa1wri=mLZ3bTu3}gqIEkJ)u!Zn8a>COdCbzivBg~2j`FsJ(
ztRd70_xY!{7W+7j=h=PBg9)=G34Be9Ouu|OK{K_&U|gV1Z|ZsVjO*6TD*F>II?Y0`
zju(X)8}%nhzI*z$+S_`5X*Tl-qwU$gHyt4y_w{0)3U#OBAT?eOe>yldLHISWdKOq=
z;q9BRB=8|p{|Zs&Jv}>HZbA^KHh!u5ToL~S8&-eEN=Z_x1BLoH<V*OIN3dzf5K{=X
z64T@F9#y=~d>=fmde7ij%9BDJrP#u(C;%`@REQu@j=ro<U<x#e3hXcfod2A)h)TW&
z9M_XRe7LvIas05w_njk9&P#(-{oD1rUzjto>B8`+ZNHl~Htk*mK(C~uE(buAYya2y
zs^b~JL}t;^(eB=g7a+CF$C(ZWz>m<xcIb-m=&{%<OZsK{vVh>Cpf{NGhzNaQ;w<GM
z^BdGYBW`Ra_1^*-<_yiKv<RG5l*wrMMHWl^gr<DynozX=lcE4E>uff+&AAooHBT7~
z0AYhoyt9zm_xv?JVpi0WY#820K4g3JSVe5(G=8XZ%O#{eAk%WlNg0YMYbb4_hmUnv
zzio%9!fSm?aMwsZoJKRToe(k=mf0xQIG^6WUoy)A9os^Tepp~2?lRF7%+Cz?C(BQo
zHEJ4h^)LDg9s;Pr_6KT2yH{)_?o9qH9sE#gVCOy|@5HfCtjS_4_h4PAM;(Qtyj@a#
z<F%Tv<NC`^*VIvin(n6FqbLGAPDiGV`(q>}J`S0M(dmw5lbWQ0SGewcx~8mEIMz%>
z&he$%^3WF@;NrL-;(*&Pk2^D1znIqfooC%qbUQb!*vw=8yPt@gB&CJOz<S^x_(Af@
zi1oHTd5*f-xbZPZ^hTD4U;IC@N7|lSpG$Qw-^b1yW|GtQ(QnZA&$r2r@9s{>yVUaJ
za5R4}*p!(Pc>u|DNcTOvY0}3)e^%XmSgzIm{F<h<ZI{5DWso}yWQzH7m|$8On_b(;
z8zbsgRX>vWQtr<-^r2{^z}m=RnL$%cyhXBNel?_tri*+!`lV>n5p+4V|L(5W6oGe1
zX5CJj_ucX%nvLuFpZ3p)e;h+lIMDSh_rCfpFmGq1PCTSiYV9LNJ;aQiv}U^D8zNa~
z)p{FtD{7Dv!}9|2I|Ec`JgXy=$z{@&A2z(3{=p+cJze_xRvWVFk-{m{ZbOy}`#tKh
zG-6=}X^gSbn!xd|XY|!Nnx4+`kX7ZM55MqT;bG8M?Q0?`QRZ`f6mucoNz$6rWDudc
zVN|x})0klYHM5B(PO#$=kF>&0Sh`(aKjOMIr|&?!G2g*PqWLrEBUujX1$F#u_U1>D
z{nFTF=;gL*<bgIDZ~HT_?=hJ%+d_OW>~-9bRu%mTwskRtB&SBUF|qsyT^~amlC`h?
zBd<~eJ`stl<E~d}M!;3(TkF~5jbzww(7gC+&AO!l))oFQy#zX*$m)|dTL;Z>vf%b;
zOU+M{C6=a?#`^~Uz6Y4vx+@QAHNIuJ4;_o+qkR2xTBYkP4X+)dl-q?2s5ne2*G84+
zHo%Gp<<sL=b>}irOQA4}%|1gL;gwiW;l0Qov%zD%L?OfB9&wyq2BTtXweM&m`c4Eu
z6i*E=E_a---|<COqF6)RiW5}&g$`YWX?#=9IC3jovQ!ZS+<Un`4)O(h4KKAq&kefL
zX~J+>^lEj3#-X$!IpW_c7nEEQu+N=_=j>)<W^4MzU0Hfuz8c`{il^XX2Na!u!mHn9
zDT5(Ow0K##!JJOKJ5Rv6r1&s$>RXB|o_L*aH{)Wo>timX(>JTNH-CD{zLA*(2t|}?
zr&fKrsUe^=FGZg*YKXVzo*-)QSWWAFU`R{bZ-x}nug05e?J%=8ttc=oY+80R=(ycF
z+`Q+*L_`=ck_5mAnL;6EwmJSF6~5p{Jl(nFoa`)YG@K0!iJkTSHs{9P@=7i}py3sb
z4UwSVKe5R}h<|o+T20Q_=5e|Oit8GpCM3_wT0H%9NeNzcY=Q&XTeik#j*m{!AJ>E*
zOHtr)`dd}9n@N<AdO{Jj){<yHo*mI*ZceuSi);0w46toIN#Dl$qVvY&!~C47#{}6~
ziMPRXep|MroG8*XJJPdGouTcWA|FA&@_`E15$$BT!$>r!BqkfYlhkD1Xf}oBgZkPB
zb!ss-4`;518QeU=l?c&7LX$rB7w3;hStUasGp`&&um%$f?<M5T>CCs_?BbdeSkl^O
z&X<`uCb$0mb12)s+Oym?$dRuSeDzUs!Qd#n_q}3UORV`Pz;{+Ik+*z!H-Rg#EtX7)
zYyDYVj0(K^h$kBkr(%Q1yiHS*`mIni$oco+s~_k8*7Q4(WyK&tUH`2HZ)U4zzF?Yt
zNf3k`nIEaoWs?Zr1o11HV8poJce@y1g3v-N%N!E}J!~H#)6!g_ADU+a9_y!4(CHt4
zf_my4Mg9J!FJw`fdex|79O55t>DHLw{Bt8#Fi~_(`(|*n^U`cDxaD=?uMM-!i`$6r
zhNCJK1Al4DiJ^{Fts@R^jHx|%YaeI}EHBz^Rr8=wu)bwa+Dv$99XCiUv0G@FR0%?J
zW7*B2?}gi^bl@*rF=#{<(UW@9gfD|KuO)V)c!S-yJ-6k(=5(;Lj+P>+hcKCwrr<UD
z=c?2!NL+-=>OB0cNuYhYoG^Pnge0If%t?MH2H{)uYL89}WLk4J#6z1V<L~)UmSG90
z&psYzXgV#~n1Ek=?Zu5viKnS>5ckRp>kaKik-Kcz@K}<yAIYxjF`RfLSoiEUdu4kl
zZxGh}YMk+2@xQo1<N5eNwIi*-5%~DJ=E^7#q@zVple4$!sQK=g$CQV0;OXufD{PcN
z$dV4L;C}^6t=B)bp>Ytriy<`t!bp6T4;b##H*<;vI8N2HVw4|d3hO9MQ+#f)uHD%&
zW=Y2dQJH`(*yA8{=~7XJp(y^L5x}rD7!KjT#}<f|A`;;*-qTwq(?6d9?r79vS__X6
z!;k(=c>K>8pZ|)>pd-ZnqTOEY)GggBb7cIm(3FDo5>d1on+YbkCwya#sM=%?gAyR0
z|3md4`R}MCCQ^`jWYSf>6H%|E+Qc`>D}*tvSm^3%<mmVpOs&nq(u*J_&Gn7Sl5|?0
ze(}jYqh5}aIX+Fnpz{-E*2~jj9b*GMMV1Cod>*4J+W&E)5a^-iBHbe*J(k77;@|bd
zEehqw1Wa9>H|K6YUY4@E*o~J+p3Tq6OgsmJAyA9~5NUPupDDJ!r#1zWi~kKc{4)z$
z-Lq=+(z?6_4{0&nMp&wQW^|Rt5%TCrwY^;pXS^#)Vs}-<J7$P9oXg0{)Vf)_tJ|Zn
zEbGBxNoD2zR~bB7O5^JF!fHWiN>xP9N+t;@P?ovnzX4ldoQc^lFry5~Hg`+k`Ecge
zcM?33GRKiLSEn3tu<sIx8$O$!a-36~ZJwCns|vZDH8>^%`3KVYo9OT|X^6IL^-54`
zeP8DR<Nd4tT(s<*>F%i`SLG&?lTovLLbhK#IwN2)oB1NJAZ>C#(mlqE!fnMX+8zlP
zo!HwkxIltr^w-p;%u@ls=%I*z1sW#+Xt-YjjrSMG^?<r#^~DSk-KkdZ$HZlE(J#GF
zxu3+<i7#mnXtt^||C;w4579#L{Andj$`Q~h)v)%Q(?JWbicbF$SNkqKNwE0KC_<+r
zjyq_n+yc$G=R1?wbk4>tYyWc2qgwGB-1ZO54CS}9%AZ<pLi)Mc%OU1+5=$^6#!}U0
ztf0?bpdg~FF&n2``Jh2DKcIC=o9ZJ>|I2rL;|EiGjCAzGDBwTr+cHtZiA>T=Q^vQs
zb`y^nlv}m@`-4JWcRBi!WzlG(8gI!`9H`6R&>}}wAr(u95v!vx^47onkClujj0T$f
zf^>OvuupCtsIz`-wvV%%jd{S?_Wi<l+wyrByR6FjNqjN!!#~6a@qZ*fcIQTZO4mw`
z-jc%a)kZML+6B2Y8lFhKR17p#f9dkVRC|YmR%u>;QNF1RonP=qVV&kIr7iJ$KY^hk
zIsDmMzhZl%0*wWRlc6Q_iISTr?X(4nPg`jKS3zMtJA*zqdyVp<xar6;l|7AKV8V&F
zF*EbGce84fZP)YL1szxJ<@2t{9PS{sY+>aTsxrn0G*&bg?gKHfGe4KTGcv)?VA03<
zZe5En(l*Y8ub}-oaTnAc!_L0YNWE6#elaDE6tFg&C42It^VZ8}+NwrD<6erS)Ty>m
z(t=%eMLn~+OM+w#-Y%ZZe`g;$oNbYMmTw}9zh_KmLrOw_hmw{Xpqz`i*tvQ%8vnY$
zZXmmV0L#}1EPL8<?>NY#5y8dxNi56TN9UTC7(`kE9cK7%7$ih%8DNlTG%pXyC&%6B
zoayG$@1|wgqWihpZOBjr^DY5ykCfolV88`VLrn*A6<`jAspd6Tjd~l<D(KB1pNnE<
zHk+;pTRUI*UUl_2GHQPZ*<8oDI25k)*>*L;*o=01C~4`K-6|RcdBQJ!)XbBfQO=7@
zS-OifiK1aIq)cQPdUn*m1Uq%nd-K`NV91dlzlQD*9Ye|jQGVk_Yp7$}WamKPD(mim
zszc_@c@9G=%|cm;r2KQ4R7H58AJqrffCshaY^23PLn8{RWlFdUY$TU7!&GXRl@JU)
z*qpd7g}lCkQ?1$j)l*}_rZ@#BMBd+@`SEv3HPYkL76ezSl~ba6<~IW~&Q;bCCkDn6
z5HMJ9TE9QBtc)0m(@w$w5Vr+ZHeQ?@J%c<qv3PT08keqho;aFI1_j}MlArkKp#Q!K
zP`}4qN6^>Pi`{rdqhX1cO+A}AfniFV{43S?L)fS;$td%%PEM^w<q<$cb#RuGIWKRQ
z&`K}$_2GrzNE-BB+ve90Ex)<KJbrT#NG?krb;z#Yf01VnDatxNzv=p6`J7Vph?{D<
zJj{7BjSPS816qU*H9yCKmL@oyb!blEH)XI7jr8F&;8e@K8SMgd%(^Nt;nf#od3J&x
zt$h~e8%BTwiFIyS_e`)w)IcKaYhsnh-tujW1*m7@l$!YKJsuf5^yzNGU*R)DH6(R5
zj(_n~%fsS5hBOB1c2{0t(TMII^#t9&@5S_iw?yNBc0F$l6t%K6=b2OJ#w%gmrXNao
zb3G;xF5WQ4?eV(>1+zcS37V<tV${M9n1-iV5LioJv4j_#n8w3v&S!fWSHv=)ZSY}k
z2Yo+f4bi*y6RIlhq_Sq(XWc3XRQ>LC+<a)O&(G88dgM^{t7M=3KCGSBFUdM2-(z=k
z=%Q5}rC6&IX3=+g5_F7qW`(Y%mR`w;ug>J8W(k>~o4!rANVz8P(Gt0lL0NRPF9e$$
zC;1llM!?g99E;naJW(T4G+l)WbkOGj8xFK|^mV=^sp;_kycx^(7uHS3@TDuQ1G?32
z8gL1G76c$KSZ7d=gMZw>#gle;a0FpA)_uJ>-@MEU_y!I$ciXMHU;M;B>^NLu^q#H<
zs*0mOk`mWe`-O&*L6^+DG$|-`7%#!qc?A+$I&KjvRQ&_ST=3p8cPS5(!3+E+jH%j@
z-70Na$nDH<Rqq}pi*g&W_xx6Ul@!kBirsNV*c(%SO8j`$Ne-i2egQ3127JEwTdN*Y
zDq31$00b~?sF*#lFvS!zbAUxSJ#CppUc7!5K9^EPBgYTwBa)Z(k^e81sDFn^F6%x2
zo8&I?K$H@qGyKzFgi64&FcB1NMm>A;x00hA@}ZT&Hs7)#``Ni?pwuR98{+8HRx5Ix
zv77$heo=6Cd_c9?CFz5A`aBkVRr^Ety==b~Di4=E`Nm?z>)5!sbmNEA%|WnAE4>=k
zM~`Ck(jp`jLECU<GlFSq-)3DlEb*1i{>p%$xVSC7b>un`zRGlv<wmK<q98=J)+@cA
zyZs8wP;O|Fr6alq5-+iG=j5PB&M6!ks#;WYT$TXse^8)cH0~8i7#62^Q1wv8#K0wb
z%g^rR3Uz8WU%f}hm;|2U<Di?y+fWye1EXP&A0W!aTeb{e8?}r7B5xNRKRzk+<K;a5
z(4C~gLIZqYJ5Pzg^~zyN5tSqzEmv@ytaSOjwo&Hb+nQiZpJ(k~?b-B;9(?H)Oh;8Y
zD7Im$aN$r!fz9ZtMDbd=_dGL1D5XWc>6H=<kzM~^_;!M`M;npYegokySV-zeZK_J$
z{qonDyTrdmGGm_4Bx9FWeQv6bj*cf#>OX0&`(t7Pe}fv-Gi9VPb%*k{LBl)ajPj&7
zZTb9xA14?Ip|eN>{vbXkj<b6rN;(^nfGY3+o$HIMwxlL71V>JV;FqU6uk}F3d)Le{
zu75wVJl9Zz*?P*Jeq44tzKj-EMO$5yGN@(TH}*DZ2ZC5lH#W%jVlH&TG1ZxBJIOKf
zSIaF0_q0mj1=>_02fTKLimc>)Yq7_RpW2g-7&O<);iFl_m^K#CC2zOBxH|(8w%omp
z*t%ao+o-Z!2K}xJo!@-#B9a?;LdPJ4OCC$R^wQWlii-+ZNDMC}6Vd<?<!(aU79)P2
zJO>c=G51eU!xKfS7=!KJ9$-Hz(93LAb8wR@0&2*t&gTIj(z+lhgphR((9`m38gBxz
z5>8^L>Qz^%vhHXZ52HjcZVAzxix`*$<>{%&xl;6Ofm^`@Y>GSf*z;K+ViT+3_2>*r
zZA&+TzJ^2D<XEssJlfVGz^g1|C>0Cn(t;Xqj$g1iq|&LDWbL^JC2Sri*ZraZcY$?I
zlz2CkKso*~Cl#jtI`Tm9OX8u6UUb;K`|3Hnx%fVl)Z*>fS+WYi99-y#)>Xnrm!)~d
zYAbzEs8b-lCVNohD6kgwV#hVlbCFrt0xyRI-Y4JFu-$fcAd9V1^J^r+_>7E(K3S3n
z%hKUOP6$F+QU^0bd;N*hVkt*6k2~A1Je|J+mjTnuf~^8GlZ^Gtetrvg0C_rV{&AqL
zAzdQ95VdG|4|_<Fo2JwApVu~<x~_juN=}nzZGIwoQj^x-D2kDnw<uqlbB~R3bFCNX
zIJ$8dvsc%I<tM77)qfyl5N3d!lHzmvB3t%)i{%4HbyU_w4{+sv-V^@u@dn@Mnvt<F
zdzgipQs5-8H@~pGbF|puO0@MXz|f+>JMhll)2LZoT331K<lb`Mp)5+V=VA4exVEm*
zZ{rtGLYGQHmFr)TBVhYJ=~{4qtvxmK2#JYjjwd)+!MLgvm^twb{>+a`TM)H294evP
zP$hGEt7B!uS$I?SX6DF+By$V0U2*(#8m~msphPW-yqx;4i_gC-{cYf;LRkSu`MyXI
zY?)k}ZjcABw1dol_`J(ZPL%hGz%eAd>&|}N8%Z580a@oTk_X7vBO?#3rP>KI#I#Gh
zY1_^VzBERr-@s&}tmEplr&NOU-V>%i>5NS6zkh9eJq-EXFrNc_K(sRjr#cdGXtu0}
zr>06q+IBl>o<w#77yOFpeL5-cG)Cn~%qEw{b&djxO3uI#%)^Dt%^^hB#fNU1b3$~i
z?+`ZBA#V3-BjW*o^*q-5ZLA))=*}-=+F0+e;Y#xxniu;Wu8)EGb!gOFT8zci_m`k6
zQ^4x<DQ*(I-5pCyjB)<0Sabg3<J7x`Oke}q%B2Xo^IT6KU=c;8DSi(aALo3^@2Fp@
zn|4NUBO4Shz*@P+&pnH2Qca3v^f%Dc>mCX|5<2<BF3UJ1_q&BiY7=Y)ojHGA7`Vjz
zR}>e4mzS42rZ@g%HHXio{Z}1PGw4b4%18)4$g6UgK&HK|9+H(>FFgnDQdQ-q%?8_u
z^f0jU%Y7Q!uPV3JeL<j1`{mpt<?0D%HA%1u(03$Q6UD@{1a=ry7+H6M_9M)}R%#z!
zN9oa$psIstsHsuR6j*Xi<eE<ipyi7ZpcBaJeNA-l{ko4XSd6=vh_aJ(Pm3b?hV4tB
zN!X+0piE<khs=RyIC0Q*q&g028C_4)DZ&4EoU~1#8C{4t*5oT6(27Xu@w*cSntXj0
z)_a{X>^6?;*Im%Q3~L0=FNEMN5<&zh5O5Mw@R>o0pMfR)k97BsUgCd1A1Dt$e0KLr
z>*LP^t>IzU$CJdt*5CgWRd{WTf9~#kNO48bqZFtv{$>f>*iEnJnM3oxE55>9E-bMG
z7!$?<QyCA?W`R+D!0|i>bAV0TijMv5*r?I3UtiCFwpNU(b=oDt5tiutlJ61kz`#J2
x|DpY*dIZz;1wIVLY=L?yANfC1!!Rmc$Q=;PH}sY6Qec5U$bC&Eq=I?q{{l&cxsU(=
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 3295661..9fa4031 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 0/9] Dynamic memzones
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
` (9 preceding siblings ...)
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (10 more replies)
10 siblings, 11 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v6:
- fix bad patch for rte_memzone_free
v5:
- Fix rte_memzone_free
- Improve rte_memzone_free unit test
v4:
- Rebase and fix couple of merge issues
v3:
- Create dummy librte_malloc
- Add deprecation notice
- Rework some of the code
- Doc update
- checkpatch
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
Sergio Gonzalez Monroy (9):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove free_memseg and references to it
eal: new rte_memzone_free
app/test: rte_memzone_free unit test
doc: announce ABI change of librte_malloc
doc: update malloc documentation
MAINTAINERS | 9 +-
app/test/test_malloc.c | 86 ----
app/test/test_memzone.c | 456 ++++------------------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 -----------
doc/guides/prog_guide/overview.rst | 11 +-
doc/guides/rel_notes/abi.rst | 1 +
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 339 ++++++----------
lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 ++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 +++++++++
lib/librte_eal/common/malloc_heap.c | 206 ++++++++++
lib/librte_eal/common/malloc_heap.h | 70 ++++
lib/librte_eal/common/rte_malloc.c | 259 ++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ---------------
lib/librte_malloc/malloc_elem.h | 190 ---------
lib/librte_malloc/malloc_heap.c | 208 ----------
lib/librte_malloc/malloc_heap.h | 70 ----
lib/librte_malloc/rte_malloc.c | 228 +----------
lib/librte_malloc/rte_malloc.h | 342 ----------------
lib/librte_malloc/rte_malloc_version.map | 16 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
56 files changed, 1928 insertions(+), 2356 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-07-02 12:14 ` Thomas Monjalon
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (9 subsequent siblings)
10 siblings, 1 reply; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
Move malloc inside eal.
Create a dummy malloc library to avoid breaking applications that have
librte_malloc in their DT_NEEDED entries.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing to free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
MAINTAINERS | 9 +-
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 208 ++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 208 --------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 228 +---------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 16 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
43 files changed, 1445 insertions(+), 1423 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 54f0973..bb08e0a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -73,6 +73,7 @@ F: lib/librte_eal/common/*
F: lib/librte_eal/common/include/*
F: lib/librte_eal/common/include/generic/
F: doc/guides/prog_guide/env_abstraction_layer.rst
+F: doc/guides/prog_guide/malloc_lib.rst
F: app/test/test_alarm.c
F: app/test/test_atomic.c
F: app/test/test_byteorder.c
@@ -97,6 +98,8 @@ F: app/test/test_spinlock.c
F: app/test/test_string_fns.c
F: app/test/test_tailq.c
F: app/test/test_version.c
+F: app/test/test_malloc.c
+F: app/test/test_func_reentrancy.c
Secondary process
K: RTE_PROC_
@@ -155,12 +158,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
Core Libraries
--------------
-Dynamic memory
-F: lib/librte_malloc/
-F: doc/guides/prog_guide/malloc_lib.rst
-F: app/test/test_malloc.c
-F: app/test/test_func_reentrancy.c
-
Memory pool
M: Olivier Matz <olivier.matz@6wind.com>
F: lib/librte_mempool/
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 464250b..2b0c877 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -102,6 +102,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -300,13 +302,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index aae22f4..fc6dc2e 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -105,6 +105,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -307,13 +309,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 97b364a..d45aa9d 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..2055539 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
+DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index c73ffb6..0cc9f8a 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -78,6 +77,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 67b6a6c..0401be2 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -47,6 +49,7 @@ DPDK_2.0 {
rte_eal_tailq_register;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -62,6 +65,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -75,6 +85,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -88,6 +99,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 38772d4..0c43d6a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..8861d27
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,208 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index e99d7a3..9e66442 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -90,6 +89,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 3696cb1..57454e6 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -52,6 +52,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_jhash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
index 947e41c..32d86b9 100644
--- a/lib/librte_malloc/Makefile
+++ b/lib/librte_malloc/Makefile
@@ -28,7 +28,6 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
include $(RTE_SDK)/mk/rte.vars.mk
# library name
@@ -41,10 +40,7 @@ CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
EXPORT_MAP := rte_malloc_version.map
# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
+SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c
# this lib needs eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index 8861d27..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
index c313a57..4b9dc7f 100644
--- a/lib/librte_malloc/rte_malloc.c
+++ b/lib/librte_malloc/rte_malloc.c
@@ -31,230 +31,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
+/* Empty file to be able to create a dummy library for deprecation policy */
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
index af6ae9b..63cb5fc 100644
--- a/lib/librte_malloc/rte_malloc_version.map
+++ b/lib/librte_malloc/rte_malloc_version.map
@@ -1,19 +1,3 @@
DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
local: *;
};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 2/9] eal: memzone allocated by malloc
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (8 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically
contiguous hugepages, memzones are slices of memsegs and malloc further
slices memzones into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 274 ++++++----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/malloc_elem.c | 68 ++++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 140 ++++++-----
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
8 files changed, 197 insertions(+), 317 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index aee184a..943012b 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,15 +50,15 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
const struct rte_mem_config *mcfg;
+ const struct rte_memzone *mz;
unsigned i = 0;
/* get pointer to global configuration */
@@ -68,8 +68,9 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ mz = &mcfg->memzone[i];
+ if (mz->addr != NULL && !strncmp(name, mz->name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
@@ -88,39 +89,45 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
len, socket_id, flags, RTE_CACHE_LINE_SIZE);
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* Find the heap with the greatest free block size */
+static void
+find_heap_max_free_elem(int *s, size_t *len, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > *len) {
+ *len = stats.greatest_free_size;
+ *s = i;
+ }
+ }
+ *len -= (MALLOC_ELEM_OVERHEAD + align);
+}
- while (addr_offset + len < ms->len) {
+/* Find a heap that can allocate the requested size */
+static void
+find_heap_suitable(int *s, size_t len, unsigned align)
+{
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size >= len + MALLOC_ELEM_OVERHEAD + align) {
+ *s = i;
+ break;
+ }
}
-
- return addr_offset;
}
static const struct rte_memzone *
@@ -128,13 +135,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -166,7 +167,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -180,129 +180,50 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = 0;
+ }
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- /* check flags for hugepage sizes */
- if ((flags & RTE_MEMZONE_2MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_1G)
- continue;
- if ((flags & RTE_MEMZONE_1GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_2M)
- continue;
- if ((flags & RTE_MEMZONE_16MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16G)
- continue;
- if ((flags & RTE_MEMZONE_16GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16M)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY) {
+ if (requested_len == 0)
+ find_heap_max_free_elem(&socket_id, &requested_len, align);
+ else
+ find_heap_suitable(&socket_id, requested_len, align);
+
+ if (socket_id == SOCKET_ID_ANY) {
+ rte_errno = ENOMEM;
+ return NULL;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
- /*
- * If RTE_MEMZONE_SIZE_HINT_ONLY flag is specified,
- * try allocating again without the size parameter otherwise -fail.
- */
- if ((flags & RTE_MEMZONE_SIZE_HINT_ONLY) &&
- ((flags & RTE_MEMZONE_1GB) || (flags & RTE_MEMZONE_2MB)
- || (flags & RTE_MEMZONE_16MB) || (flags & RTE_MEMZONE_16GB)))
- return memzone_reserve_aligned_thread_unsafe(name,
- len, socket_id, 0, align, bound);
-
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0 ? elem->size : requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -419,45 +340,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -465,14 +347,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -485,33 +363,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index 8861d27..f5fff96 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,104 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, size_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned ret = 1;
+
+ if ((flags & RTE_MEMZONE_2MB) && hugepage_sz == RTE_PGSIZE_1G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_1GB) && hugepage_sz == RTE_PGSIZE_2M)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16MB) && hugepage_sz == RTE_PGSIZE_16G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16GB) && hugepage_sz == RTE_PGSIZE_16M)
+ ret = 0;
+
+ return ret;
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -206,3 +186,21 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
socket_stats->alloc_count = heap->alloc_count;
return 0;
}
+
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 3/9] app/test: update malloc/memzone unit tests
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (7 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 440 ++++--------------------------------------------
2 files changed, 35 insertions(+), 491 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..6934eee 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
-
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
+ maxlen = find_max_block_free_size(align);
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -762,282 +680,6 @@ test_memzone_bounded(void)
if ((rc = check_memzone_bounded("bounded_1K_MAX", 0, 64, 1024)) != 0)
return (rc);
- return (0);
-}
-
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
-
return 0;
}
@@ -1125,14 +767,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +783,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
` (6 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
During initializaio malloc sets all available memory as part of the heaps.
CONFIG_RTE_MALLOC_MEMZONE_SIZE was used to specify the default memory
block size to expand the heap. The option is not used/relevant anymore,
so we remove it.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 2b0c877..a54957d 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -103,7 +103,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index fc6dc2e..72611c9 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -106,7 +106,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 5/9] eal: remove free_memseg and references to it
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (5 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
Remove free_memseg field from internal mem config structure as it is
not used anymore.
Also remove code in ivshmem that was setting up free_memseg on init.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/include/rte_eal_memconfig.h | 3 ---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
2 files changed, 12 deletions(-)
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..7de906b 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,9 +73,6 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors - NOT USED */
- struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
-
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
/* Heaps of Malloc per socket */
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 6/9] eal: new rte_memzone_free
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
` (4 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 ++
lib/librte_eal/common/eal_common_memzone.c | 67 ++++++++++++++++++++++-
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 ++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 8 +--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 ++
6 files changed, 92 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 0401be2..7110816 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -105,3 +105,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 943012b..5bc4ab4 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -77,6 +77,23 @@ memzone_lookup_thread_unsafe(const char *name)
return NULL;
}
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr == NULL)
+ return &mcfg->memzone[i];
+ }
+
+ return NULL;
+}
+
/*
* Return a pointer to a correctly filled memzone descriptor. If the
* allocation cannot be done, return NULL.
@@ -141,7 +158,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -215,7 +232,16 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+
+ if (mz == NULL) {
+ RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
+ "in config!\n", __func__);
+ rte_errno = ENOSPC;
+ return NULL;
+ }
+
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -291,6 +317,41 @@ rte_memzone_reserve_bounded(const char *name, size_t len,
return mz;
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_write_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ if (addr == NULL)
+ ret = -EINVAL;
+ else if (mcfg->memzone_cnt == 0) {
+ rte_panic("%s(): memzone address not NULL but memzone_cnt is 0!\n",
+ __func__);
+ } else {
+ memset(&mcfg->memzone[idx], 0, sizeof(mcfg->memzone[idx]));
+ mcfg->memzone_cnt--;
+ }
+
+ rte_rwlock_write_unlock(&mcfg->mlock);
+
+ rte_free(addr);
+
+ return ret;
+}
/*
* Lookup for the memzone identified by the given name
@@ -364,7 +425,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index ee62680..5223b1e 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -240,6 +240,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = mcfg->memzone_cnt;
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < mcfg->memzone_cnt; i++) {
mz = &mcfg->memzone[i];
/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 7/9] app/test: rte_memzone_free unit test
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
` (3 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
Add new unit test for rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 6934eee..c37f950 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -432,7 +432,6 @@ test_memzone_reserve_max(void)
printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -472,7 +471,6 @@ test_memzone_reserve_max_aligned(void)
maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -684,6 +682,82 @@ test_memzone_bounded(void)
}
static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[RTE_MAX_MEMZONE];
+ int i;
+ char name[20];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "tempzone%u", i);
+ mz[i] = rte_memzone_reserve(name, 1, SOCKET_ID_ANY, 0);
+ } while (mz[i++] != NULL);
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ mz[0] = rte_memzone_reserve("tempzone0new", 0, SOCKET_ID_ANY, 0);
+
+ if (mz[0] == NULL) {
+ printf("Fail to create memzone - tempzone0new - when MAX memzones were "
+ "created and one was free\n");
+ return -1;
+ }
+
+ for (i = i - 2; i >= 0; i--) {
+ if (rte_memzone_free(mz[i])) {
+ printf("Fail memzone free - tempzone%d\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
test_memzone(void)
{
const struct rte_memzone *memzone1;
@@ -763,6 +837,10 @@ test_memzone(void)
if (mz != NULL)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
printf("test reserving memzone with bigger size than the maximum\n");
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 8/9] doc: announce ABI change of librte_malloc
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
` (2 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
Announce the creation of dummy malloc library for 2.1 and removal of
such library, now integrated in librte_eal, for 2.2 release.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/rel_notes/abi.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/guides/rel_notes/abi.rst b/doc/guides/rel_notes/abi.rst
index f00a6ee..2aaf900 100644
--- a/doc/guides/rel_notes/abi.rst
+++ b/doc/guides/rel_notes/abi.rst
@@ -38,3 +38,4 @@ Examples of Deprecation Notices
Deprecation Notices
-------------------
+* librte_malloc library has been integrated into librte_eal. The 2.1 release creates a dummy/empty malloc library to fulfill binaries with dynamic linking dependencies on librte_malloc.so. Such dummy library will not be created from release 2.2 so binaries will need to be rebuilt.
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v6 9/9] doc: update malloc documentation
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
@ 2015-06-26 15:29 ` Sergio Gonzalez Monroy
2015-06-26 16:13 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Ananyev, Konstantin
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-06-26 15:29 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/prog_guide/env_abstraction_layer.rst | 220 +++++++++++++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------------------
doc/guides/prog_guide/overview.rst | 11 +-
5 files changed, 221 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..f3506081c91feb7b80b0389c80e06addafcfb907 100644
GIT binary patch
literal 80952
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
literal 81329
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%0AJ1PuixJX??D}PX8AKL
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
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 3295661..9fa4031 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v6 0/9] Dynamic memzones
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (8 preceding siblings ...)
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
@ 2015-06-26 16:13 ` Ananyev, Konstantin
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
10 siblings, 0 replies; 108+ messages in thread
From: Ananyev, Konstantin @ 2015-06-26 16:13 UTC (permalink / raw)
To: Gonzalez Monroy, Sergio, dev
> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Sergio Gonzalez Monroy
> Sent: Friday, June 26, 2015 4:29 PM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v6 0/9] Dynamic memzones
>
> Current implemetation allows reserving/creating memzones but not the opposite
> (unreserve/free). This affects mempools and other memzone based objects.
>
> From my point of view, implementing free functionality for memzones would look
> like malloc over memsegs.
> Thus, this approach moves malloc inside eal (which in turn removes a circular
> dependency), where malloc heaps are composed of memsegs.
> We keep both malloc and memzone APIs as they are, but memzones allocate its
> memory by calling malloc_heap_alloc.
> Some extra functionality is required in malloc to allow for boundary constrained
> memory requests.
> In summary, currently malloc is based on memzones, and with this approach
> memzones are based on malloc.
>
> v6:
> - fix bad patch for rte_memzone_free
>
> v5:
> - Fix rte_memzone_free
> - Improve rte_memzone_free unit test
>
> v4:
> - Rebase and fix couple of merge issues
>
> v3:
> - Create dummy librte_malloc
> - Add deprecation notice
> - Rework some of the code
> - Doc update
> - checkpatch
>
> v2:
> - New rte_memzone_free
> - Support memzone len = 0
> - Add all available memsegs to malloc heap at init
> - Update memzone/malloc unit tests
>
> Sergio Gonzalez Monroy (9):
> eal: move librte_malloc to eal/common
> eal: memzone allocated by malloc
> app/test: update malloc/memzone unit tests
> config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
> eal: remove free_memseg and references to it
> eal: new rte_memzone_free
> app/test: rte_memzone_free unit test
> doc: announce ABI change of librte_malloc
> doc: update malloc documentation
>
> MAINTAINERS | 9 +-
> app/test/test_malloc.c | 86 ----
> app/test/test_memzone.c | 456 ++++------------------
> config/common_bsdapp | 8 +-
> config/common_linuxapp | 8 +-
> doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++++++++-
> doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
> doc/guides/prog_guide/index.rst | 1 -
> doc/guides/prog_guide/malloc_lib.rst | 233 -----------
> doc/guides/prog_guide/overview.rst | 11 +-
> doc/guides/rel_notes/abi.rst | 1 +
> drivers/net/af_packet/Makefile | 1 -
> drivers/net/bonding/Makefile | 1 -
> drivers/net/e1000/Makefile | 2 +-
> drivers/net/enic/Makefile | 2 +-
> drivers/net/fm10k/Makefile | 2 +-
> drivers/net/i40e/Makefile | 2 +-
> drivers/net/ixgbe/Makefile | 2 +-
> drivers/net/mlx4/Makefile | 1 -
> drivers/net/null/Makefile | 1 -
> drivers/net/pcap/Makefile | 1 -
> drivers/net/virtio/Makefile | 2 +-
> drivers/net/vmxnet3/Makefile | 2 +-
> drivers/net/xenvirt/Makefile | 2 +-
> lib/Makefile | 2 +-
> lib/librte_acl/Makefile | 2 +-
> lib/librte_eal/bsdapp/eal/Makefile | 4 +-
> lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
> lib/librte_eal/common/Makefile | 1 +
> lib/librte_eal/common/eal_common_memzone.c | 339 ++++++----------
> lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
> lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++
> lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
> lib/librte_eal/common/include/rte_memzone.h | 11 +
> lib/librte_eal/common/malloc_elem.c | 344 ++++++++++++++++
> lib/librte_eal/common/malloc_elem.h | 192 +++++++++
> lib/librte_eal/common/malloc_heap.c | 206 ++++++++++
> lib/librte_eal/common/malloc_heap.h | 70 ++++
> lib/librte_eal/common/rte_malloc.c | 259 ++++++++++++
> lib/librte_eal/linuxapp/eal/Makefile | 4 +-
> lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
> lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
> lib/librte_hash/Makefile | 2 +-
> lib/librte_lpm/Makefile | 2 +-
> lib/librte_malloc/Makefile | 6 +-
> lib/librte_malloc/malloc_elem.c | 320 ---------------
> lib/librte_malloc/malloc_elem.h | 190 ---------
> lib/librte_malloc/malloc_heap.c | 208 ----------
> lib/librte_malloc/malloc_heap.h | 70 ----
> lib/librte_malloc/rte_malloc.c | 228 +----------
> lib/librte_malloc/rte_malloc.h | 342 ----------------
> lib/librte_malloc/rte_malloc_version.map | 16 -
> lib/librte_mempool/Makefile | 2 -
> lib/librte_port/Makefile | 1 -
> lib/librte_ring/Makefile | 3 +-
> lib/librte_table/Makefile | 1 -
> 56 files changed, 1928 insertions(+), 2356 deletions(-)
> delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
> create mode 100644 lib/librte_eal/common/include/rte_malloc.h
> create mode 100644 lib/librte_eal/common/malloc_elem.c
> create mode 100644 lib/librte_eal/common/malloc_elem.h
> create mode 100644 lib/librte_eal/common/malloc_heap.c
> create mode 100644 lib/librte_eal/common/malloc_heap.h
> create mode 100644 lib/librte_eal/common/rte_malloc.c
> delete mode 100644 lib/librte_malloc/malloc_elem.c
> delete mode 100644 lib/librte_malloc/malloc_elem.h
> delete mode 100644 lib/librte_malloc/malloc_heap.c
> delete mode 100644 lib/librte_malloc/malloc_heap.h
> delete mode 100644 lib/librte_malloc/rte_malloc.h
>
> --
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
> 1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-07-02 12:14 ` Thomas Monjalon
2015-07-03 8:16 ` Gonzalez Monroy, Sergio
0 siblings, 1 reply; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-02 12:14 UTC (permalink / raw)
To: Sergio Gonzalez Monroy; +Cc: dev
2015-06-26 16:29, Sergio Gonzalez Monroy:
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -73,6 +73,7 @@ F: lib/librte_eal/common/*
> F: lib/librte_eal/common/include/*
> F: lib/librte_eal/common/include/generic/
> F: doc/guides/prog_guide/env_abstraction_layer.rst
> +F: doc/guides/prog_guide/malloc_lib.rst
> F: app/test/test_alarm.c
> F: app/test/test_atomic.c
> F: app/test/test_byteorder.c
> @@ -97,6 +98,8 @@ F: app/test/test_spinlock.c
> F: app/test/test_string_fns.c
> F: app/test/test_tailq.c
> F: app/test/test_version.c
> +F: app/test/test_malloc.c
> +F: app/test/test_func_reentrancy.c
I think we should keep a separate maintainer section for memory allocator
in EAL. I suggest this:
Memory allocation
M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
F: lib/librte_eal/common/include/rte_mem*
F: lib/librte_eal/common/include/rte_malloc.h
F: lib/librte_eal/common/*malloc*
F: lib/librte_eal/common/eal_common_mem*
F: lib/librte_eal/common/eal_hugepages.h
F: doc/guides/prog_guide/malloc_lib.rst
F: app/test/test_malloc.c
F: app/test/test_func_reentrancy.c
> Secondary process
> K: RTE_PROC_
> @@ -155,12 +158,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
> Core Libraries
> --------------
>
> -Dynamic memory
> -F: lib/librte_malloc/
> -F: doc/guides/prog_guide/malloc_lib.rst
> -F: app/test/test_malloc.c
> -F: app/test/test_func_reentrancy.c
> -
> Memory pool
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common
2015-07-02 12:14 ` Thomas Monjalon
@ 2015-07-03 8:16 ` Gonzalez Monroy, Sergio
2015-07-03 9:02 ` Thomas Monjalon
0 siblings, 1 reply; 108+ messages in thread
From: Gonzalez Monroy, Sergio @ 2015-07-03 8:16 UTC (permalink / raw)
To: Thomas Monjalon; +Cc: dev
On 02/07/2015 13:14, Thomas Monjalon wrote:
> 2015-06-26 16:29, Sergio Gonzalez Monroy:
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -73,6 +73,7 @@ F: lib/librte_eal/common/*
>> F: lib/librte_eal/common/include/*
>> F: lib/librte_eal/common/include/generic/
>> F: doc/guides/prog_guide/env_abstraction_layer.rst
>> +F: doc/guides/prog_guide/malloc_lib.rst
>> F: app/test/test_alarm.c
>> F: app/test/test_atomic.c
>> F: app/test/test_byteorder.c
>> @@ -97,6 +98,8 @@ F: app/test/test_spinlock.c
>> F: app/test/test_string_fns.c
>> F: app/test/test_tailq.c
>> F: app/test/test_version.c
>> +F: app/test/test_malloc.c
>> +F: app/test/test_func_reentrancy.c
> I think we should keep a separate maintainer section for memory allocator
> in EAL. I suggest this:
>
> Memory allocation
> M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
> F: lib/librte_eal/common/include/rte_mem*
> F: lib/librte_eal/common/include/rte_malloc.h
> F: lib/librte_eal/common/*malloc*
> F: lib/librte_eal/common/eal_common_mem*
> F: lib/librte_eal/common/eal_hugepages.h
> F: doc/guides/prog_guide/malloc_lib.rst
> F: app/test/test_malloc.c
> F: app/test/test_func_reentrancy.c
>
>
Fine with me.
Do you need a new version of the patches with that change?
Sergio
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common
2015-07-03 8:16 ` Gonzalez Monroy, Sergio
@ 2015-07-03 9:02 ` Thomas Monjalon
0 siblings, 0 replies; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-03 9:02 UTC (permalink / raw)
To: Gonzalez Monroy, Sergio; +Cc: dev
2015-07-03 09:16, Gonzalez Monroy, Sergio:
> On 02/07/2015 13:14, Thomas Monjalon wrote:
> > 2015-06-26 16:29, Sergio Gonzalez Monroy:
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -73,6 +73,7 @@ F: lib/librte_eal/common/*
> >> F: lib/librte_eal/common/include/*
> >> F: lib/librte_eal/common/include/generic/
> >> F: doc/guides/prog_guide/env_abstraction_layer.rst
> >> +F: doc/guides/prog_guide/malloc_lib.rst
> >> F: app/test/test_alarm.c
> >> F: app/test/test_atomic.c
> >> F: app/test/test_byteorder.c
> >> @@ -97,6 +98,8 @@ F: app/test/test_spinlock.c
> >> F: app/test/test_string_fns.c
> >> F: app/test/test_tailq.c
> >> F: app/test/test_version.c
> >> +F: app/test/test_malloc.c
> >> +F: app/test/test_func_reentrancy.c
> > I think we should keep a separate maintainer section for memory allocator
> > in EAL. I suggest this:
> >
> > Memory allocation
> > M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
> > F: lib/librte_eal/common/include/rte_mem*
> > F: lib/librte_eal/common/include/rte_malloc.h
> > F: lib/librte_eal/common/*malloc*
> > F: lib/librte_eal/common/eal_common_mem*
> > F: lib/librte_eal/common/eal_hugepages.h
> > F: doc/guides/prog_guide/malloc_lib.rst
> > F: app/test/test_malloc.c
> > F: app/test/test_func_reentrancy.c
> >
> >
> Fine with me.
> Do you need a new version of the patches with that change?
Yes please.
Thanks for your involvement.
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 0/9] Dynamic memzones
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (9 preceding siblings ...)
2015-06-26 16:13 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Ananyev, Konstantin
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (10 more replies)
10 siblings, 11 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v7:
- Create a separated maintainer section for memory allocation
v6:
- Fix bad patch for rte_memzone_free
v5:
- Fix rte_memzone_free
- Improve rte_memzone_free unit test
v4:
- Rebase and fix couple of merge issues
v3:
- Create dummy librte_malloc
- Add deprecation notice
- Rework some of the code
- Doc update
- checkpatch
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
Sergio Gonzalez Monroy (9):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove free_memseg and references to it
eal: new rte_memzone_free
app/test: rte_memzone_free unit test
doc: announce ABI change of librte_malloc
doc: update malloc documentation
MAINTAINERS | 22 +-
app/test/test_malloc.c | 86 ----
app/test/test_memzone.c | 456 ++++------------------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 -----------
doc/guides/prog_guide/overview.rst | 11 +-
doc/guides/rel_notes/abi.rst | 1 +
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 339 ++++++----------
lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 ++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 +++++++++
lib/librte_eal/common/malloc_heap.c | 206 ++++++++++
lib/librte_eal/common/malloc_heap.h | 70 ++++
lib/librte_eal/common/rte_malloc.c | 259 ++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ---------------
lib/librte_malloc/malloc_elem.h | 190 ---------
lib/librte_malloc/malloc_heap.c | 208 ----------
lib/librte_malloc/malloc_heap.h | 70 ----
lib/librte_malloc/rte_malloc.c | 228 +----------
lib/librte_malloc/rte_malloc.h | 342 ----------------
lib/librte_malloc/rte_malloc_version.map | 16 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
56 files changed, 1938 insertions(+), 2359 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 1/9] eal: move librte_malloc to eal/common
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (9 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
Move malloc inside eal and create a new section in MAINTAINERS file for
Memory Allocation in EAL.
Create a dummy malloc library to avoid breaking applications that have
librte_malloc in their DT_NEEDED entries.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing to free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
MAINTAINERS | 22 +-
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 208 ++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 208 --------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 228 +---------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 16 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
43 files changed, 1455 insertions(+), 1426 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 5476a73..6e69d13 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -83,12 +83,9 @@ F: app/test/test_debug.c
F: app/test/test_devargs.c
F: app/test/test_eal*
F: app/test/test_errno.c
-F: app/test/test_func_reentrancy.c
F: app/test/test_interrupts.c
F: app/test/test_logs.c
F: app/test/test_memcpy*
-F: app/test/test_memory.c
-F: app/test/test_memzone.c
F: app/test/test_pci.c
F: app/test/test_per_lcore.c
F: app/test/test_prefetch.c
@@ -98,6 +95,19 @@ F: app/test/test_string_fns.c
F: app/test/test_tailq.c
F: app/test/test_version.c
+Memory Allocation
+M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
+F: lib/librte_eal/common/include/rte_mem*
+F: lib/librte_eal/common/include/rte_malloc.h
+F: lib/librte_eal/common/*malloc*
+F: lib/librte_eal/common/eal_common_mem*
+F: lib/librte_eal/common/eal_hugepages.h
+F: doc/guides/prog_guide/malloc_lib.rst
+F: app/test/test_func_reentrancy.c
+F: app/test/test_malloc.c
+F: app/test/test_memory.c
+F: app/test/test_memzone.c
+
Secondary process
K: RTE_PROC_
F: doc/guides/prog_guide/multi_proc_support.rst
@@ -156,12 +166,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
Core Libraries
--------------
-Dynamic memory
-F: lib/librte_malloc/
-F: doc/guides/prog_guide/malloc_lib.rst
-F: app/test/test_malloc.c
-F: app/test/test_func_reentrancy.c
-
Memory pool
M: Olivier Matz <olivier.matz@6wind.com>
F: lib/librte_mempool/
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 78754b2..c7262e1 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -102,6 +102,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -310,13 +312,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index f5646e0..70117e7 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -105,6 +105,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -317,13 +319,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 725717f..14cb53f 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..2055539 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
+DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index c73ffb6..0cc9f8a 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -78,6 +77,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 67b6a6c..0401be2 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -47,6 +49,7 @@ DPDK_2.0 {
rte_eal_tailq_register;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -62,6 +65,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -75,6 +85,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -88,6 +99,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 38772d4..0c43d6a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..8861d27
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,208 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index e99d7a3..9e66442 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -90,6 +89,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 981230b..52abeea 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -53,6 +53,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_thash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
index 947e41c..32d86b9 100644
--- a/lib/librte_malloc/Makefile
+++ b/lib/librte_malloc/Makefile
@@ -28,7 +28,6 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
include $(RTE_SDK)/mk/rte.vars.mk
# library name
@@ -41,10 +40,7 @@ CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
EXPORT_MAP := rte_malloc_version.map
# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
+SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c
# this lib needs eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index 8861d27..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
index c313a57..4b9dc7f 100644
--- a/lib/librte_malloc/rte_malloc.c
+++ b/lib/librte_malloc/rte_malloc.c
@@ -31,230 +31,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
+/* Empty file to be able to create a dummy library for deprecation policy */
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
index af6ae9b..63cb5fc 100644
--- a/lib/librte_malloc/rte_malloc_version.map
+++ b/lib/librte_malloc/rte_malloc_version.map
@@ -1,19 +1,3 @@
DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
local: *;
};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 2/9] eal: memzone allocated by malloc
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (8 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically
contiguous hugepages, memzones are slices of memsegs and malloc further
slices memzones into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 274 ++++++----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/malloc_elem.c | 68 ++++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 140 ++++++-----
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
8 files changed, 197 insertions(+), 317 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index aee184a..943012b 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,15 +50,15 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
const struct rte_mem_config *mcfg;
+ const struct rte_memzone *mz;
unsigned i = 0;
/* get pointer to global configuration */
@@ -68,8 +68,9 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ mz = &mcfg->memzone[i];
+ if (mz->addr != NULL && !strncmp(name, mz->name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
@@ -88,39 +89,45 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
len, socket_id, flags, RTE_CACHE_LINE_SIZE);
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* Find the heap with the greatest free block size */
+static void
+find_heap_max_free_elem(int *s, size_t *len, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > *len) {
+ *len = stats.greatest_free_size;
+ *s = i;
+ }
+ }
+ *len -= (MALLOC_ELEM_OVERHEAD + align);
+}
- while (addr_offset + len < ms->len) {
+/* Find a heap that can allocate the requested size */
+static void
+find_heap_suitable(int *s, size_t len, unsigned align)
+{
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size >= len + MALLOC_ELEM_OVERHEAD + align) {
+ *s = i;
+ break;
+ }
}
-
- return addr_offset;
}
static const struct rte_memzone *
@@ -128,13 +135,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -166,7 +167,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -180,129 +180,50 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = 0;
+ }
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- /* check flags for hugepage sizes */
- if ((flags & RTE_MEMZONE_2MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_1G)
- continue;
- if ((flags & RTE_MEMZONE_1GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_2M)
- continue;
- if ((flags & RTE_MEMZONE_16MB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16G)
- continue;
- if ((flags & RTE_MEMZONE_16GB) &&
- free_memseg[i].hugepage_sz == RTE_PGSIZE_16M)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY) {
+ if (requested_len == 0)
+ find_heap_max_free_elem(&socket_id, &requested_len, align);
+ else
+ find_heap_suitable(&socket_id, requested_len, align);
+
+ if (socket_id == SOCKET_ID_ANY) {
+ rte_errno = ENOMEM;
+ return NULL;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
- /*
- * If RTE_MEMZONE_SIZE_HINT_ONLY flag is specified,
- * try allocating again without the size parameter otherwise -fail.
- */
- if ((flags & RTE_MEMZONE_SIZE_HINT_ONLY) &&
- ((flags & RTE_MEMZONE_1GB) || (flags & RTE_MEMZONE_2MB)
- || (flags & RTE_MEMZONE_16MB) || (flags & RTE_MEMZONE_16GB)))
- return memzone_reserve_aligned_thread_unsafe(name,
- len, socket_id, 0, align, bound);
-
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0 ? elem->size : requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -419,45 +340,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -465,14 +347,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -485,33 +363,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index 8861d27..f5fff96 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,104 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, size_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned ret = 1;
+
+ if ((flags & RTE_MEMZONE_2MB) && hugepage_sz == RTE_PGSIZE_1G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_1GB) && hugepage_sz == RTE_PGSIZE_2M)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16MB) && hugepage_sz == RTE_PGSIZE_16G)
+ ret = 0;
+ if ((flags & RTE_MEMZONE_16GB) && hugepage_sz == RTE_PGSIZE_16M)
+ ret = 0;
+
+ return ret;
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -206,3 +186,21 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
socket_stats->alloc_count = heap->alloc_count;
return 0;
}
+
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 3/9] app/test: update malloc/memzone unit tests
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (7 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 440 ++++--------------------------------------------
2 files changed, 35 insertions(+), 491 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..6934eee 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
-
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
+ maxlen = find_max_block_free_size(align);
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -762,282 +680,6 @@ test_memzone_bounded(void)
if ((rc = check_memzone_bounded("bounded_1K_MAX", 0, 64, 1024)) != 0)
return (rc);
- return (0);
-}
-
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
-
return 0;
}
@@ -1125,14 +767,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +783,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
` (6 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
During initializaio malloc sets all available memory as part of the heaps.
CONFIG_RTE_MALLOC_MEMZONE_SIZE was used to specify the default memory
block size to expand the heap. The option is not used/relevant anymore,
so we remove it.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index c7262e1..1d89e08 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -103,7 +103,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 70117e7..5deb029 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -106,7 +106,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 5/9] eal: remove free_memseg and references to it
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (5 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
Remove free_memseg field from internal mem config structure as it is
not used anymore.
Also remove code in ivshmem that was setting up free_memseg on init.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/include/rte_eal_memconfig.h | 3 ---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
2 files changed, 12 deletions(-)
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..7de906b 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,9 +73,6 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors - NOT USED */
- struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
-
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
/* Heaps of Malloc per socket */
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 6/9] eal: new rte_memzone_free
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
` (4 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 ++
lib/librte_eal/common/eal_common_memzone.c | 67 ++++++++++++++++++++++-
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 ++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 8 +--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 ++
6 files changed, 92 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 0401be2..7110816 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -105,3 +105,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 943012b..5bc4ab4 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -77,6 +77,23 @@ memzone_lookup_thread_unsafe(const char *name)
return NULL;
}
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr == NULL)
+ return &mcfg->memzone[i];
+ }
+
+ return NULL;
+}
+
/*
* Return a pointer to a correctly filled memzone descriptor. If the
* allocation cannot be done, return NULL.
@@ -141,7 +158,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -215,7 +232,16 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+
+ if (mz == NULL) {
+ RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
+ "in config!\n", __func__);
+ rte_errno = ENOSPC;
+ return NULL;
+ }
+
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -291,6 +317,41 @@ rte_memzone_reserve_bounded(const char *name, size_t len,
return mz;
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_write_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ if (addr == NULL)
+ ret = -EINVAL;
+ else if (mcfg->memzone_cnt == 0) {
+ rte_panic("%s(): memzone address not NULL but memzone_cnt is 0!\n",
+ __func__);
+ } else {
+ memset(&mcfg->memzone[idx], 0, sizeof(mcfg->memzone[idx]));
+ mcfg->memzone_cnt--;
+ }
+
+ rte_rwlock_write_unlock(&mcfg->mlock);
+
+ rte_free(addr);
+
+ return ret;
+}
/*
* Lookup for the memzone identified by the given name
@@ -364,7 +425,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index ee62680..5223b1e 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -240,6 +240,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = mcfg->memzone_cnt;
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < mcfg->memzone_cnt; i++) {
mz = &mcfg->memzone[i];
/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 7/9] app/test: rte_memzone_free unit test
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
` (3 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
Add new unit test for rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 6934eee..c37f950 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -432,7 +432,6 @@ test_memzone_reserve_max(void)
printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -472,7 +471,6 @@ test_memzone_reserve_max_aligned(void)
maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -684,6 +682,82 @@ test_memzone_bounded(void)
}
static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[RTE_MAX_MEMZONE];
+ int i;
+ char name[20];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "tempzone%u", i);
+ mz[i] = rte_memzone_reserve(name, 1, SOCKET_ID_ANY, 0);
+ } while (mz[i++] != NULL);
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ mz[0] = rte_memzone_reserve("tempzone0new", 0, SOCKET_ID_ANY, 0);
+
+ if (mz[0] == NULL) {
+ printf("Fail to create memzone - tempzone0new - when MAX memzones were "
+ "created and one was free\n");
+ return -1;
+ }
+
+ for (i = i - 2; i >= 0; i--) {
+ if (rte_memzone_free(mz[i])) {
+ printf("Fail memzone free - tempzone%d\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
test_memzone(void)
{
const struct rte_memzone *memzone1;
@@ -763,6 +837,10 @@ test_memzone(void)
if (mz != NULL)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
printf("test reserving memzone with bigger size than the maximum\n");
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 8/9] doc: announce ABI change of librte_malloc
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
` (2 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
Announce the creation of dummy malloc library for 2.1 and removal of
such library, now integrated in librte_eal, for 2.2 release.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/rel_notes/abi.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/doc/guides/rel_notes/abi.rst b/doc/guides/rel_notes/abi.rst
index 110c486..50fb6a5 100644
--- a/doc/guides/rel_notes/abi.rst
+++ b/doc/guides/rel_notes/abi.rst
@@ -12,3 +12,4 @@ Examples of Deprecation Notices
Deprecation Notices
-------------------
+* librte_malloc library has been integrated into librte_eal. The 2.1 release creates a dummy/empty malloc library to fulfill binaries with dynamic linking dependencies on librte_malloc.so. Such dummy library will not be created from release 2.2 so binaries will need to be rebuilt.
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v7 9/9] doc: update malloc documentation
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
@ 2015-07-03 9:55 ` Sergio Gonzalez Monroy
2015-07-13 13:15 ` [dpdk-dev] [PATCH v7 0/9] Dynamic memzones Thomas Monjalon
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-03 9:55 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/prog_guide/env_abstraction_layer.rst | 220 +++++++++++++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------------------
doc/guides/prog_guide/overview.rst | 11 +-
5 files changed, 221 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..f3506081c91feb7b80b0389c80e06addafcfb907 100644
GIT binary patch
literal 80952
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
literal 81329
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%0AJ1PuixJX??D}PX8AKL
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
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 3295661..9fa4031 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v7 0/9] Dynamic memzones
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
` (8 preceding siblings ...)
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
@ 2015-07-13 13:15 ` Thomas Monjalon
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
10 siblings, 0 replies; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-13 13:15 UTC (permalink / raw)
To: Sergio Gonzalez Monroy; +Cc: dev
2015-07-03 10:55, Sergio Gonzalez Monroy:
> Current implemetation allows reserving/creating memzones but not the opposite
> (unreserve/free). This affects mempools and other memzone based objects.
>
> From my point of view, implementing free functionality for memzones would look
> like malloc over memsegs.
> Thus, this approach moves malloc inside eal (which in turn removes a circular
> dependency), where malloc heaps are composed of memsegs.
> We keep both malloc and memzone APIs as they are, but memzones allocate its
> memory by calling malloc_heap_alloc.
> Some extra functionality is required in malloc to allow for boundary constrained
> memory requests.
> In summary, currently malloc is based on memzones, and with this approach
> memzones are based on malloc.
Please Sergio, could you rebase your work on top of TileGX series
which made some important modifications in memzones?
The TileGX series is integrated first. The goal is to integrate this series
as soon as possible.
Thanks
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 0/9] Dynamic memzones
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
` (9 preceding siblings ...)
2015-07-13 13:15 ` [dpdk-dev] [PATCH v7 0/9] Dynamic memzones Thomas Monjalon
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (10 more replies)
10 siblings, 11 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v8:
- Rebase against current HEAD to factor for changes made by new Tile-Gx arch
v7:
- Create a separated maintainer section for memory allocation
v6:
- Fix bad patch for rte_memzone_free
v5:
- Fix rte_memzone_free
- Improve rte_memzone_free unit test
v4:
- Rebase and fix couple of merge issues
v3:
- Create dummy librte_malloc
- Add deprecation notice
- Rework some of the code
- Doc update
- checkpatch
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
v6 Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Sergio Gonzalez Monroy (9):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove free_memseg and references to it
eal: new rte_memzone_free
app/test: rte_memzone_free unit test
doc: announce ABI change of librte_malloc
doc: update malloc documentation
MAINTAINERS | 22 +-
app/test/test_malloc.c | 86 ----
app/test/test_memzone.c | 456 ++++------------------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 -----------
doc/guides/prog_guide/overview.rst | 11 +-
doc/guides/rel_notes/abi.rst | 6 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 353 +++++++----------
lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 ++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 +++++++++
lib/librte_eal/common/malloc_heap.c | 227 +++++++++++
lib/librte_eal/common/malloc_heap.h | 70 ++++
lib/librte_eal/common/rte_malloc.c | 259 ++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ---------------
lib/librte_malloc/malloc_elem.h | 190 ---------
lib/librte_malloc/malloc_heap.c | 208 ----------
lib/librte_malloc/malloc_heap.h | 70 ----
lib/librte_malloc/rte_malloc.c | 228 +----------
lib/librte_malloc/rte_malloc.h | 342 ----------------
lib/librte_malloc/rte_malloc_version.map | 16 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
56 files changed, 1965 insertions(+), 2372 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 1/9] eal: move librte_malloc to eal/common
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (9 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
Move malloc inside eal and create a new section in MAINTAINERS file for
Memory Allocation in EAL.
Create a dummy malloc library to avoid breaking applications that have
librte_malloc in their DT_NEEDED entries.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing to free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
MAINTAINERS | 22 +-
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 208 ++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 208 --------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 228 +---------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 16 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
43 files changed, 1455 insertions(+), 1426 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index a15105d..52892d0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -83,12 +83,9 @@ F: app/test/test_debug.c
F: app/test/test_devargs.c
F: app/test/test_eal*
F: app/test/test_errno.c
-F: app/test/test_func_reentrancy.c
F: app/test/test_interrupts.c
F: app/test/test_logs.c
F: app/test/test_memcpy*
-F: app/test/test_memory.c
-F: app/test/test_memzone.c
F: app/test/test_pci.c
F: app/test/test_per_lcore.c
F: app/test/test_prefetch.c
@@ -98,6 +95,19 @@ F: app/test/test_string_fns.c
F: app/test/test_tailq.c
F: app/test/test_version.c
+Memory Allocation
+M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
+F: lib/librte_eal/common/include/rte_mem*
+F: lib/librte_eal/common/include/rte_malloc.h
+F: lib/librte_eal/common/*malloc*
+F: lib/librte_eal/common/eal_common_mem*
+F: lib/librte_eal/common/eal_hugepages.h
+F: doc/guides/prog_guide/malloc_lib.rst
+F: app/test/test_func_reentrancy.c
+F: app/test/test_malloc.c
+F: app/test/test_memory.c
+F: app/test/test_memzone.c
+
Secondary process
K: RTE_PROC_
F: doc/guides/prog_guide/multi_proc_support.rst
@@ -161,12 +171,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
Core Libraries
--------------
-Dynamic memory
-F: lib/librte_malloc/
-F: doc/guides/prog_guide/malloc_lib.rst
-F: app/test/test_malloc.c
-F: app/test/test_func_reentrancy.c
-
Memory pool
M: Olivier Matz <olivier.matz@6wind.com>
F: lib/librte_mempool/
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 7112f1c..5bb7f55 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -106,6 +106,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -308,13 +310,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 4d90d35..7b57044 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -109,6 +109,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -316,13 +318,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 725717f..14cb53f 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..2055539 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
+DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index 40ec648..064b0c5 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -79,6 +78,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 38772d4..0c43d6a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..8861d27
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,208 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 42a16fe..00ed62e 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -91,6 +90,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 039da46..4bb3848 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -53,6 +53,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_thash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal and ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc lib/librte_ring
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_ring
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
index 947e41c..32d86b9 100644
--- a/lib/librte_malloc/Makefile
+++ b/lib/librte_malloc/Makefile
@@ -28,7 +28,6 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
include $(RTE_SDK)/mk/rte.vars.mk
# library name
@@ -41,10 +40,7 @@ CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
EXPORT_MAP := rte_malloc_version.map
# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
+SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c
# this lib needs eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index 8861d27..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
index c313a57..4b9dc7f 100644
--- a/lib/librte_malloc/rte_malloc.c
+++ b/lib/librte_malloc/rte_malloc.c
@@ -31,230 +31,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
+/* Empty file to be able to create a dummy library for deprecation policy */
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
index af6ae9b..63cb5fc 100644
--- a/lib/librte_malloc/rte_malloc_version.map
+++ b/lib/librte_malloc/rte_malloc_version.map
@@ -1,19 +1,3 @@
DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
local: *;
};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 2/9] eal: memzone allocated by malloc
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 20:41 ` Thomas Monjalon
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (8 subsequent siblings)
10 siblings, 1 reply; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically
contiguous hugepages, memzones are slices of memsegs and malloc further
slices memzones into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 289 +++++-----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/malloc_elem.c | 68 +++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 161 ++++++------
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
8 files changed, 220 insertions(+), 330 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 9c1da71..fd7e73f 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,15 +50,15 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
const struct rte_mem_config *mcfg;
+ const struct rte_memzone *mz;
unsigned i = 0;
/* get pointer to global configuration */
@@ -68,62 +68,62 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ mz = &mcfg->memzone[i];
+ if (mz->addr != NULL && !strncmp(name, mz->name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
return NULL;
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* Find the heap with the greatest free block size */
+static void
+find_heap_max_free_elem(int *s, size_t *len, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > *len) {
+ *len = stats.greatest_free_size;
+ *s = i;
+ }
+ }
+ *len -= (MALLOC_ELEM_OVERHEAD + align);
+}
- while (addr_offset + len < ms->len) {
+/* Find a heap that can allocate the requested size */
+static void
+find_heap_suitable(int *s, size_t len, unsigned align)
+{
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size >= len + MALLOC_ELEM_OVERHEAD + align) {
+ *s = i;
+ break;
+ }
}
-
- return addr_offset;
}
static const struct rte_memzone *
memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
- int socket_id, uint64_t size_mask, unsigned align,
- unsigned bound)
+ int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -155,7 +155,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -169,108 +168,50 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = 0;
+ }
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- if ((size_mask & free_memseg[i].hugepage_sz) == 0)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY) {
+ if (requested_len == 0)
+ find_heap_max_free_elem(&socket_id, &requested_len, align);
+ else
+ find_heap_suitable(&socket_id, requested_len, align);
+
+ if (socket_id == SOCKET_ID_ANY) {
+ rte_errno = ENOMEM;
+ return NULL;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0 ? elem->size : requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -282,26 +223,6 @@ rte_memzone_reserve_thread_safe(const char *name, size_t len,
{
struct rte_mem_config *mcfg;
const struct rte_memzone *mz = NULL;
- uint64_t size_mask = 0;
-
- if (flags & RTE_MEMZONE_256KB)
- size_mask |= RTE_PGSIZE_256K;
- if (flags & RTE_MEMZONE_2MB)
- size_mask |= RTE_PGSIZE_2M;
- if (flags & RTE_MEMZONE_16MB)
- size_mask |= RTE_PGSIZE_16M;
- if (flags & RTE_MEMZONE_256MB)
- size_mask |= RTE_PGSIZE_256M;
- if (flags & RTE_MEMZONE_512MB)
- size_mask |= RTE_PGSIZE_512M;
- if (flags & RTE_MEMZONE_1GB)
- size_mask |= RTE_PGSIZE_1G;
- if (flags & RTE_MEMZONE_4GB)
- size_mask |= RTE_PGSIZE_4G;
- if (flags & RTE_MEMZONE_16GB)
- size_mask |= RTE_PGSIZE_16G;
- if (!size_mask)
- size_mask = UINT64_MAX;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -309,18 +230,7 @@ rte_memzone_reserve_thread_safe(const char *name, size_t len,
rte_rwlock_write_lock(&mcfg->mlock);
mz = memzone_reserve_aligned_thread_unsafe(
- name, len, socket_id, size_mask, align, bound);
-
- /*
- * If we failed to allocate the requested page size, and the
- * RTE_MEMZONE_SIZE_HINT_ONLY flag is specified, try allocating
- * again.
- */
- if (!mz && rte_errno == ENOMEM && size_mask != UINT64_MAX &&
- flags & RTE_MEMZONE_SIZE_HINT_ONLY) {
- mz = memzone_reserve_aligned_thread_unsafe(
- name, len, socket_id, UINT64_MAX, align, bound);
- }
+ name, len, socket_id, flags, align, bound);
rte_rwlock_write_unlock(&mcfg->mlock);
@@ -412,45 +322,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -458,14 +329,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -478,33 +345,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index 8861d27..2496b77 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,125 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, size_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned check_flag = 0;
+
+ if (!(flags & ~RTE_MEMZONE_SIZE_HINT_ONLY))
+ return 1;
+
+ switch (hugepage_sz) {
+ case RTE_PGSIZE_256K:
+ check_flag = RTE_MEMZONE_256KB;
+ break;
+ case RTE_PGSIZE_2M:
+ check_flag = RTE_MEMZONE_2MB;
+ break;
+ case RTE_PGSIZE_16M:
+ check_flag = RTE_MEMZONE_16MB;
+ break;
+ case RTE_PGSIZE_256M:
+ check_flag = RTE_MEMZONE_256MB;
+ break;
+ case RTE_PGSIZE_512M:
+ check_flag = RTE_MEMZONE_512MB;
+ break;
+ case RTE_PGSIZE_1G:
+ check_flag = RTE_MEMZONE_1GB;
+ break;
+ case RTE_PGSIZE_4G:
+ check_flag = RTE_MEMZONE_4GB;
+ break;
+ case RTE_PGSIZE_16G:
+ check_flag = RTE_MEMZONE_16GB;
+ }
+
+ return (check_flag & flags);
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ if (alt_elem == NULL)
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -206,3 +207,21 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
socket_stats->alloc_count = heap->alloc_count;
return 0;
}
+
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 3/9] app/test: update malloc/memzone unit tests
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (7 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 440 ++++--------------------------------------------
2 files changed, 35 insertions(+), 491 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..6934eee 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
-
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
+ maxlen = find_max_block_free_size(align);
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -762,282 +680,6 @@ test_memzone_bounded(void)
if ((rc = check_memzone_bounded("bounded_1K_MAX", 0, 64, 1024)) != 0)
return (rc);
- return (0);
-}
-
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
-
return 0;
}
@@ -1125,14 +767,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +783,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
` (6 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
During initializaio malloc sets all available memory as part of the heaps.
CONFIG_RTE_MALLOC_MEMZONE_SIZE was used to specify the default memory
block size to expand the heap. The option is not used/relevant anymore,
so we remove it.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 5bb7f55..4e505bf 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -107,7 +107,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 7b57044..579a5d7 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -110,7 +110,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 5/9] eal: remove free_memseg and references to it
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (5 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
Remove free_memseg field from internal mem config structure as it is
not used anymore.
Also remove code in ivshmem that was setting up free_memseg on init.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/include/rte_eal_memconfig.h | 3 ---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
2 files changed, 12 deletions(-)
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..7de906b 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,9 +73,6 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors - NOT USED */
- struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
-
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
/* Heaps of Malloc per socket */
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 6/9] eal: new rte_memzone_free
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
` (4 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 ++
lib/librte_eal/common/eal_common_memzone.c | 68 ++++++++++++++++++++++-
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 ++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 8 +--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 ++
6 files changed, 93 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index fd7e73f..a7efd5e 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -77,6 +77,23 @@ memzone_lookup_thread_unsafe(const char *name)
return NULL;
}
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr == NULL)
+ return &mcfg->memzone[i];
+ }
+
+ return NULL;
+}
+
/* Find the heap with the greatest free block size */
static void
find_heap_max_free_elem(int *s, size_t *len, unsigned align)
@@ -129,7 +146,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -203,7 +220,16 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+
+ if (mz == NULL) {
+ RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
+ "in config!\n", __func__);
+ rte_errno = ENOSPC;
+ return NULL;
+ }
+
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -274,6 +300,42 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
flags, RTE_CACHE_LINE_SIZE, 0);
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_write_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ if (addr == NULL)
+ ret = -EINVAL;
+ else if (mcfg->memzone_cnt == 0) {
+ rte_panic("%s(): memzone address not NULL but memzone_cnt is 0!\n",
+ __func__);
+ } else {
+ memset(&mcfg->memzone[idx], 0, sizeof(mcfg->memzone[idx]));
+ mcfg->memzone_cnt--;
+ }
+
+ rte_rwlock_write_unlock(&mcfg->mlock);
+
+ rte_free(addr);
+
+ return ret;
+}
+
/*
* Lookup for the memzone identified by the given name
*/
@@ -346,7 +408,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index de5ae55..38e5f5b 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -256,6 +256,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = mcfg->memzone_cnt;
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < mcfg->memzone_cnt; i++) {
mz = &mcfg->memzone[i];
/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 7/9] app/test: rte_memzone_free unit test
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
` (3 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
Add new unit test for rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 6934eee..c37f950 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -432,7 +432,6 @@ test_memzone_reserve_max(void)
printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -472,7 +471,6 @@ test_memzone_reserve_max_aligned(void)
maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -684,6 +682,82 @@ test_memzone_bounded(void)
}
static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[RTE_MAX_MEMZONE];
+ int i;
+ char name[20];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "tempzone%u", i);
+ mz[i] = rte_memzone_reserve(name, 1, SOCKET_ID_ANY, 0);
+ } while (mz[i++] != NULL);
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ mz[0] = rte_memzone_reserve("tempzone0new", 0, SOCKET_ID_ANY, 0);
+
+ if (mz[0] == NULL) {
+ printf("Fail to create memzone - tempzone0new - when MAX memzones were "
+ "created and one was free\n");
+ return -1;
+ }
+
+ for (i = i - 2; i >= 0; i--) {
+ if (rte_memzone_free(mz[i])) {
+ printf("Fail memzone free - tempzone%d\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
test_memzone(void)
{
const struct rte_memzone *memzone1;
@@ -763,6 +837,10 @@ test_memzone(void)
if (mz != NULL)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
printf("test reserving memzone with bigger size than the maximum\n");
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 8/9] doc: announce ABI change of librte_malloc
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
` (2 subsequent siblings)
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
Announce the creation of dummy malloc library for 2.1 and removal of
such library, now integrated in librte_eal, for 2.2 release.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/rel_notes/abi.rst | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/doc/guides/rel_notes/abi.rst b/doc/guides/rel_notes/abi.rst
index 931e785..76e0ae2 100644
--- a/doc/guides/rel_notes/abi.rst
+++ b/doc/guides/rel_notes/abi.rst
@@ -16,7 +16,6 @@ Examples of Deprecation Notices
Deprecation Notices
-------------------
-
* Significant ABI changes are planned for struct rte_eth_dev to support up to
1024 queues per port. This change will be in release 2.2.
There is no backward compatibility planned from release 2.2.
@@ -24,3 +23,8 @@ Deprecation Notices
* The Macros RTE_HASH_BUCKET_ENTRIES_MAX and RTE_HASH_KEY_LENGTH_MAX are
deprecated and will be removed with version 2.2.
+
+* librte_malloc library has been integrated into librte_eal. The 2.1 release
+ creates a dummy/empty malloc library to fulfill binaries with dynamic linking
+ dependencies on librte_malloc.so. Such dummy library will not be created from
+ release 2.2 so binaries will need to be rebuilt.
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v8 9/9] doc: update malloc documentation
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
@ 2015-07-14 8:57 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-10-14 0:12 ` [dpdk-dev] [PATCH v8 " Stephen Hemminger
10 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-14 8:57 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/prog_guide/env_abstraction_layer.rst | 220 +++++++++++++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------------------
doc/guides/prog_guide/overview.rst | 11 +-
5 files changed, 221 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..f3506081c91feb7b80b0389c80e06addafcfb907 100644
GIT binary patch
literal 80952
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
literal 81329
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%0AJ1PuixJX??D}PX8AKL
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
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 036640c..176f2c2 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v8 2/9] eal: memzone allocated by malloc
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-07-14 20:41 ` Thomas Monjalon
0 siblings, 0 replies; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-14 20:41 UTC (permalink / raw)
To: Sergio Gonzalez Monroy; +Cc: dev
There is an error with 32-bit build:
lib/librte_eal/common/malloc_heap.c:83:2: error: large integer implicitly truncated to unsigned type [-Werror=overflow]
case RTE_PGSIZE_4G:
^
lib/librte_eal/common/malloc_heap.c:86:2: error: large integer implicitly truncated to unsigned type [-Werror=overflow]
case RTE_PGSIZE_16G:
^
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 0/9] Dynamic memzones
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
` (8 preceding siblings ...)
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (9 more replies)
2015-10-14 0:12 ` [dpdk-dev] [PATCH v8 " Stephen Hemminger
10 siblings, 10 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v9:
- Fix incorrect size_t type that results in 32bits compilation error.
v8:
- Rebase against current HEAD to factor for changes made by new Tile-Gx arch
v7:
- Create a separated maintainer section for memory allocation
v6:
- Fix bad patch for rte_memzone_free
v5:
- Fix rte_memzone_free
- Improve rte_memzone_free unit test
v4:
- Rebase and fix couple of merge issues
v3:
- Create dummy librte_malloc
- Add deprecation notice
- Rework some of the code
- Doc update
- checkpatch
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
v6 Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Sergio Gonzalez Monroy (9):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove free_memseg and references to it
eal: new rte_memzone_free
app/test: rte_memzone_free unit test
doc: announce ABI change of librte_malloc
doc: update malloc documentation
MAINTAINERS | 22 +-
app/test/test_malloc.c | 86 ----
app/test/test_memzone.c | 456 ++++------------------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 -----------
doc/guides/prog_guide/overview.rst | 11 +-
doc/guides/rel_notes/abi.rst | 6 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 353 +++++++----------
lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 ++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 192 +++++++++
lib/librte_eal/common/malloc_heap.c | 227 +++++++++++
lib/librte_eal/common/malloc_heap.h | 70 ++++
lib/librte_eal/common/rte_malloc.c | 259 ++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ---------------
lib/librte_malloc/malloc_elem.h | 190 ---------
lib/librte_malloc/malloc_heap.c | 208 ----------
lib/librte_malloc/malloc_heap.h | 70 ----
lib/librte_malloc/rte_malloc.c | 228 +----------
lib/librte_malloc/rte_malloc.h | 342 ----------------
lib/librte_malloc/rte_malloc_version.map | 16 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
56 files changed, 1965 insertions(+), 2372 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 1/9] eal: move librte_malloc to eal/common
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (8 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
Move malloc inside eal and create a new section in MAINTAINERS file for
Memory Allocation in EAL.
Create a dummy malloc library to avoid breaking applications that have
librte_malloc in their DT_NEEDED entries.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing to free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
MAINTAINERS | 22 +-
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 208 ++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 208 --------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 228 +---------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 16 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
43 files changed, 1455 insertions(+), 1426 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index a15105d..52892d0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -83,12 +83,9 @@ F: app/test/test_debug.c
F: app/test/test_devargs.c
F: app/test/test_eal*
F: app/test/test_errno.c
-F: app/test/test_func_reentrancy.c
F: app/test/test_interrupts.c
F: app/test/test_logs.c
F: app/test/test_memcpy*
-F: app/test/test_memory.c
-F: app/test/test_memzone.c
F: app/test/test_pci.c
F: app/test/test_per_lcore.c
F: app/test/test_prefetch.c
@@ -98,6 +95,19 @@ F: app/test/test_string_fns.c
F: app/test/test_tailq.c
F: app/test/test_version.c
+Memory Allocation
+M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
+F: lib/librte_eal/common/include/rte_mem*
+F: lib/librte_eal/common/include/rte_malloc.h
+F: lib/librte_eal/common/*malloc*
+F: lib/librte_eal/common/eal_common_mem*
+F: lib/librte_eal/common/eal_hugepages.h
+F: doc/guides/prog_guide/malloc_lib.rst
+F: app/test/test_func_reentrancy.c
+F: app/test/test_malloc.c
+F: app/test/test_memory.c
+F: app/test/test_memzone.c
+
Secondary process
K: RTE_PROC_
F: doc/guides/prog_guide/multi_proc_support.rst
@@ -161,12 +171,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
Core Libraries
--------------
-Dynamic memory
-F: lib/librte_malloc/
-F: doc/guides/prog_guide/malloc_lib.rst
-F: app/test/test_malloc.c
-F: app/test/test_func_reentrancy.c
-
Memory pool
M: Olivier Matz <olivier.matz@6wind.com>
F: lib/librte_mempool/
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 7112f1c..5bb7f55 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -106,6 +106,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -308,13 +310,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 4d90d35..7b57044 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -109,6 +109,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -316,13 +318,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 725717f..14cb53f 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..2055539 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
+DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index 40ec648..064b0c5 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -79,6 +78,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 38772d4..0c43d6a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..8861d27
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,208 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 42a16fe..00ed62e 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -91,6 +90,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 039da46..4bb3848 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -53,6 +53,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_thash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal and ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc lib/librte_ring
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_ring
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
index 947e41c..32d86b9 100644
--- a/lib/librte_malloc/Makefile
+++ b/lib/librte_malloc/Makefile
@@ -28,7 +28,6 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
include $(RTE_SDK)/mk/rte.vars.mk
# library name
@@ -41,10 +40,7 @@ CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
EXPORT_MAP := rte_malloc_version.map
# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
+SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c
# this lib needs eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index 8861d27..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
index c313a57..4b9dc7f 100644
--- a/lib/librte_malloc/rte_malloc.c
+++ b/lib/librte_malloc/rte_malloc.c
@@ -31,230 +31,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
+/* Empty file to be able to create a dummy library for deprecation policy */
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
index af6ae9b..63cb5fc 100644
--- a/lib/librte_malloc/rte_malloc_version.map
+++ b/lib/librte_malloc/rte_malloc_version.map
@@ -1,19 +1,3 @@
DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
local: *;
};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 2/9] eal: memzone allocated by malloc
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (7 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically
contiguous hugepages, memzones are slices of memsegs and malloc further
slices memzones into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 289 +++++-----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/malloc_elem.c | 68 +++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 161 ++++++------
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 7 +-
8 files changed, 220 insertions(+), 330 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 9c1da71..fd7e73f 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,15 +50,15 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
const struct rte_mem_config *mcfg;
+ const struct rte_memzone *mz;
unsigned i = 0;
/* get pointer to global configuration */
@@ -68,62 +68,62 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ mz = &mcfg->memzone[i];
+ if (mz->addr != NULL && !strncmp(name, mz->name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
return NULL;
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* Find the heap with the greatest free block size */
+static void
+find_heap_max_free_elem(int *s, size_t *len, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > *len) {
+ *len = stats.greatest_free_size;
+ *s = i;
+ }
+ }
+ *len -= (MALLOC_ELEM_OVERHEAD + align);
+}
- while (addr_offset + len < ms->len) {
+/* Find a heap that can allocate the requested size */
+static void
+find_heap_suitable(int *s, size_t len, unsigned align)
+{
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ unsigned i;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size >= len + MALLOC_ELEM_OVERHEAD + align) {
+ *s = i;
+ break;
+ }
}
-
- return addr_offset;
}
static const struct rte_memzone *
memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
- int socket_id, uint64_t size_mask, unsigned align,
- unsigned bound)
+ int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -155,7 +155,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -169,108 +168,50 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = 0;
+ }
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
-
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
-
- if ((size_mask & free_memseg[i].hugepage_sz) == 0)
- continue;
-
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY) {
+ if (requested_len == 0)
+ find_heap_max_free_elem(&socket_id, &requested_len, align);
+ else
+ find_heap_suitable(&socket_id, requested_len, align);
+
+ if (socket_id == SOCKET_ID_ANY) {
+ rte_errno = ENOMEM;
+ return NULL;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket_id], NULL,
+ requested_len, flags, align, bound);
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0 ? elem->size : requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -282,26 +223,6 @@ rte_memzone_reserve_thread_safe(const char *name, size_t len,
{
struct rte_mem_config *mcfg;
const struct rte_memzone *mz = NULL;
- uint64_t size_mask = 0;
-
- if (flags & RTE_MEMZONE_256KB)
- size_mask |= RTE_PGSIZE_256K;
- if (flags & RTE_MEMZONE_2MB)
- size_mask |= RTE_PGSIZE_2M;
- if (flags & RTE_MEMZONE_16MB)
- size_mask |= RTE_PGSIZE_16M;
- if (flags & RTE_MEMZONE_256MB)
- size_mask |= RTE_PGSIZE_256M;
- if (flags & RTE_MEMZONE_512MB)
- size_mask |= RTE_PGSIZE_512M;
- if (flags & RTE_MEMZONE_1GB)
- size_mask |= RTE_PGSIZE_1G;
- if (flags & RTE_MEMZONE_4GB)
- size_mask |= RTE_PGSIZE_4G;
- if (flags & RTE_MEMZONE_16GB)
- size_mask |= RTE_PGSIZE_16G;
- if (!size_mask)
- size_mask = UINT64_MAX;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -309,18 +230,7 @@ rte_memzone_reserve_thread_safe(const char *name, size_t len,
rte_rwlock_write_lock(&mcfg->mlock);
mz = memzone_reserve_aligned_thread_unsafe(
- name, len, socket_id, size_mask, align, bound);
-
- /*
- * If we failed to allocate the requested page size, and the
- * RTE_MEMZONE_SIZE_HINT_ONLY flag is specified, try allocating
- * again.
- */
- if (!mz && rte_errno == ENOMEM && size_mask != UINT64_MAX &&
- flags & RTE_MEMZONE_SIZE_HINT_ONLY) {
- mz = memzone_reserve_aligned_thread_unsafe(
- name, len, socket_id, UINT64_MAX, align, bound);
- }
+ name, len, socket_id, flags, align, bound);
rte_rwlock_write_unlock(&mcfg->mlock);
@@ -412,45 +322,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -458,14 +329,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -478,33 +345,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index 8861d27..21d8914 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,125 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, uint64_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned check_flag = 0;
+
+ if (!(flags & ~RTE_MEMZONE_SIZE_HINT_ONLY))
+ return 1;
+
+ switch (hugepage_sz) {
+ case RTE_PGSIZE_256K:
+ check_flag = RTE_MEMZONE_256KB;
+ break;
+ case RTE_PGSIZE_2M:
+ check_flag = RTE_MEMZONE_2MB;
+ break;
+ case RTE_PGSIZE_16M:
+ check_flag = RTE_MEMZONE_16MB;
+ break;
+ case RTE_PGSIZE_256M:
+ check_flag = RTE_MEMZONE_256MB;
+ break;
+ case RTE_PGSIZE_512M:
+ check_flag = RTE_MEMZONE_512MB;
+ break;
+ case RTE_PGSIZE_1G:
+ check_flag = RTE_MEMZONE_1GB;
+ break;
+ case RTE_PGSIZE_4G:
+ check_flag = RTE_MEMZONE_4GB;
+ break;
+ case RTE_PGSIZE_16G:
+ check_flag = RTE_MEMZONE_16GB;
+ }
+
+ return (check_flag & flags);
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ if (alt_elem == NULL)
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -206,3 +207,21 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
socket_stats->alloc_count = heap->alloc_count;
return 0;
}
+
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..54c2bd8 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -87,7 +86,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +97,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +255,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 3/9] app/test: update malloc/memzone unit tests
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (6 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 440 ++++--------------------------------------------
2 files changed, 35 insertions(+), 491 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..6934eee 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
-
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
+ maxlen = find_max_block_free_size(align);
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -762,282 +680,6 @@ test_memzone_bounded(void)
if ((rc = check_memzone_bounded("bounded_1K_MAX", 0, 64, 1024)) != 0)
return (rc);
- return (0);
-}
-
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
-
return 0;
}
@@ -1125,14 +767,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +783,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
` (5 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
During initializaio malloc sets all available memory as part of the heaps.
CONFIG_RTE_MALLOC_MEMZONE_SIZE was used to specify the default memory
block size to expand the heap. The option is not used/relevant anymore,
so we remove it.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 5bb7f55..4e505bf 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -107,7 +107,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 7b57044..579a5d7 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -110,7 +110,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 5/9] eal: remove free_memseg and references to it
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (4 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
Remove free_memseg field from internal mem config structure as it is
not used anymore.
Also remove code in ivshmem that was setting up free_memseg on init.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/include/rte_eal_memconfig.h | 3 ---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
2 files changed, 12 deletions(-)
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..7de906b 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,9 +73,6 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors - NOT USED */
- struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
-
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
/* Heaps of Malloc per socket */
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 6/9] eal: new rte_memzone_free
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
` (3 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 ++
lib/librte_eal/common/eal_common_memzone.c | 68 ++++++++++++++++++++++-
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 ++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 8 +--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 ++
6 files changed, 93 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index fd7e73f..a7efd5e 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -77,6 +77,23 @@ memzone_lookup_thread_unsafe(const char *name)
return NULL;
}
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr == NULL)
+ return &mcfg->memzone[i];
+ }
+
+ return NULL;
+}
+
/* Find the heap with the greatest free block size */
static void
find_heap_max_free_elem(int *s, size_t *len, unsigned align)
@@ -129,7 +146,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -203,7 +220,16 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+
+ if (mz == NULL) {
+ RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
+ "in config!\n", __func__);
+ rte_errno = ENOSPC;
+ return NULL;
+ }
+
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -274,6 +300,42 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
flags, RTE_CACHE_LINE_SIZE, 0);
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_write_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ if (addr == NULL)
+ ret = -EINVAL;
+ else if (mcfg->memzone_cnt == 0) {
+ rte_panic("%s(): memzone address not NULL but memzone_cnt is 0!\n",
+ __func__);
+ } else {
+ memset(&mcfg->memzone[idx], 0, sizeof(mcfg->memzone[idx]));
+ mcfg->memzone_cnt--;
+ }
+
+ rte_rwlock_write_unlock(&mcfg->mlock);
+
+ rte_free(addr);
+
+ return ret;
+}
+
/*
* Lookup for the memzone identified by the given name
*/
@@ -346,7 +408,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index de5ae55..38e5f5b 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -256,6 +256,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = mcfg->memzone_cnt;
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < mcfg->memzone_cnt; i++) {
mz = &mcfg->memzone[i];
/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 7/9] app/test: rte_memzone_free unit test
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
` (2 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
Add new unit test for rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 6934eee..c37f950 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -432,7 +432,6 @@ test_memzone_reserve_max(void)
printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -472,7 +471,6 @@ test_memzone_reserve_max_aligned(void)
maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -684,6 +682,82 @@ test_memzone_bounded(void)
}
static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[RTE_MAX_MEMZONE];
+ int i;
+ char name[20];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "tempzone%u", i);
+ mz[i] = rte_memzone_reserve(name, 1, SOCKET_ID_ANY, 0);
+ } while (mz[i++] != NULL);
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ mz[0] = rte_memzone_reserve("tempzone0new", 0, SOCKET_ID_ANY, 0);
+
+ if (mz[0] == NULL) {
+ printf("Fail to create memzone - tempzone0new - when MAX memzones were "
+ "created and one was free\n");
+ return -1;
+ }
+
+ for (i = i - 2; i >= 0; i--) {
+ if (rte_memzone_free(mz[i])) {
+ printf("Fail memzone free - tempzone%d\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
test_memzone(void)
{
const struct rte_memzone *memzone1;
@@ -763,6 +837,10 @@ test_memzone(void)
if (mz != NULL)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
printf("test reserving memzone with bigger size than the maximum\n");
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 8/9] doc: announce ABI change of librte_malloc
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
Announce the creation of dummy malloc library for 2.1 and removal of
such library, now integrated in librte_eal, for 2.2 release.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/rel_notes/abi.rst | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/doc/guides/rel_notes/abi.rst b/doc/guides/rel_notes/abi.rst
index 931e785..76e0ae2 100644
--- a/doc/guides/rel_notes/abi.rst
+++ b/doc/guides/rel_notes/abi.rst
@@ -16,7 +16,6 @@ Examples of Deprecation Notices
Deprecation Notices
-------------------
-
* Significant ABI changes are planned for struct rte_eth_dev to support up to
1024 queues per port. This change will be in release 2.2.
There is no backward compatibility planned from release 2.2.
@@ -24,3 +23,8 @@ Deprecation Notices
* The Macros RTE_HASH_BUCKET_ENTRIES_MAX and RTE_HASH_KEY_LENGTH_MAX are
deprecated and will be removed with version 2.2.
+
+* librte_malloc library has been integrated into librte_eal. The 2.1 release
+ creates a dummy/empty malloc library to fulfill binaries with dynamic linking
+ dependencies on librte_malloc.so. Such dummy library will not be created from
+ release 2.2 so binaries will need to be rebuilt.
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v9 9/9] doc: update malloc documentation
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
@ 2015-07-15 8:26 ` Sergio Gonzalez Monroy
2015-07-15 14:07 ` Thomas Monjalon
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
9 siblings, 1 reply; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 8:26 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/prog_guide/env_abstraction_layer.rst | 220 +++++++++++++++++++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------------------------
doc/guides/prog_guide/overview.rst | 11 +-
5 files changed, 221 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..f3506081c91feb7b80b0389c80e06addafcfb907 100644
GIT binary patch
literal 80952
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
literal 81329
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%0AJ1PuixJX??D}PX8AKL
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
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 036640c..176f2c2 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v9 9/9] doc: update malloc documentation
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
@ 2015-07-15 14:07 ` Thomas Monjalon
2015-07-15 14:11 ` Gonzalez Monroy, Sergio
0 siblings, 1 reply; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-15 14:07 UTC (permalink / raw)
To: Sergio Gonzalez Monroy; +Cc: dev
2015-07-15 09:26, Sergio Gonzalez Monroy:
> Update malloc documentation to reflect new implementation details.
>
> doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
Sorry, you cannot update a PNG file without converting it as a SVG file.
Some PNG files were allowed when importing doc in git.
But we shouldn't allow it anymore because it doesn't allow others to update
the image.
Thanks
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v9 9/9] doc: update malloc documentation
2015-07-15 14:07 ` Thomas Monjalon
@ 2015-07-15 14:11 ` Gonzalez Monroy, Sergio
2015-07-15 14:48 ` Thomas Monjalon
0 siblings, 1 reply; 108+ messages in thread
From: Gonzalez Monroy, Sergio @ 2015-07-15 14:11 UTC (permalink / raw)
To: Thomas Monjalon; +Cc: dev
On 15/07/2015 15:07, Thomas Monjalon wrote:
> 2015-07-15 09:26, Sergio Gonzalez Monroy:
>> Update malloc documentation to reflect new implementation details.
>>
>> doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
> Sorry, you cannot update a PNG file without converting it as a SVG file.
> Some PNG files were allowed when importing doc in git.
> But we shouldn't allow it anymore because it doesn't allow others to update
> the image.
>
> Thanks
I don't have the original, I just modified the PNG as they were
minor/simple changes.
What are the options?
Sergio
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v9 9/9] doc: update malloc documentation
2015-07-15 14:11 ` Gonzalez Monroy, Sergio
@ 2015-07-15 14:48 ` Thomas Monjalon
0 siblings, 0 replies; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-15 14:48 UTC (permalink / raw)
To: Gonzalez Monroy, Sergio; +Cc: dev
2015-07-15 15:11, Gonzalez Monroy, Sergio:
> On 15/07/2015 15:07, Thomas Monjalon wrote:
> > 2015-07-15 09:26, Sergio Gonzalez Monroy:
> >> Update malloc documentation to reflect new implementation details.
> >>
> >> doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 80952 bytes
> > Sorry, you cannot update a PNG file without converting it as a SVG file.
> > Some PNG files were allowed when importing doc in git.
> > But we shouldn't allow it anymore because it doesn't allow others to update
> > the image.
> >
> > Thanks
> I don't have the original, I just modified the PNG as they were
> minor/simple changes.
> What are the options?
Allowing such modification would defeat the full-SVG goal.
2 options:
- no modification
- recreate the drawing
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 0/9] Dynamic memzones
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (8 preceding siblings ...)
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
` (9 more replies)
9 siblings, 10 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
Current implemetation allows reserving/creating memzones but not the opposite
(unreserve/free). This affects mempools and other memzone based objects.
>From my point of view, implementing free functionality for memzones would look
like malloc over memsegs.
Thus, this approach moves malloc inside eal (which in turn removes a circular
dependency), where malloc heaps are composed of memsegs.
We keep both malloc and memzone APIs as they are, but memzones allocate its
memory by calling malloc_heap_alloc.
Some extra functionality is required in malloc to allow for boundary constrained
memory requests.
In summary, currently malloc is based on memzones, and with this approach
memzones are based on malloc.
v10:
- Convert PNG to SVG
- Fix issue with --no-huge by forcing SOCKET_ID_ANY
- Rework some parts of the code
v9:
- Fix incorrect size_t type that results in 32bits compilation error.
v8:
- Rebase against current HEAD to factor for changes made by new Tile-Gx arch
v7:
- Create a separated maintainer section for memory allocation
v6:
- Fix bad patch for rte_memzone_free
v5:
- Fix rte_memzone_free
- Improve rte_memzone_free unit test
v4:
- Rebase and fix couple of merge issues
v3:
- Create dummy librte_malloc
- Add deprecation notice
- Rework some of the code
- Doc update
- checkpatch
v2:
- New rte_memzone_free
- Support memzone len = 0
- Add all available memsegs to malloc heap at init
- Update memzone/malloc unit tests
v6 Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Sergio Gonzalez Monroy (9):
eal: move librte_malloc to eal/common
eal: memzone allocated by malloc
app/test: update malloc/memzone unit tests
config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
eal: remove free_memseg and references to it
eal: new rte_memzone_free
app/test: rte_memzone_free unit test
doc: announce ABI change of librte_malloc
doc: update malloc documentation
MAINTAINERS | 22 +-
app/test/test_malloc.c | 86 --
app/test/test_memzone.c | 456 ++-------
config/common_bsdapp | 8 +-
config/common_linuxapp | 8 +-
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 0 bytes
doc/guides/prog_guide/img/malloc_heap.svg | 1018 +++++++++++++++++++++
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 -----
doc/guides/prog_guide/overview.rst | 11 +-
doc/guides/rel_notes/abi.rst | 6 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 19 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/eal_common_memzone.c | 352 +++----
lib/librte_eal/common/include/rte_eal_memconfig.h | 5 +-
lib/librte_eal/common/include/rte_malloc.h | 342 +++++++
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/include/rte_memzone.h | 11 +
lib/librte_eal/common/malloc_elem.c | 344 +++++++
lib/librte_eal/common/malloc_elem.h | 192 ++++
lib/librte_eal/common/malloc_heap.c | 227 +++++
lib/librte_eal/common/malloc_heap.h | 70 ++
lib/librte_eal/common/rte_malloc.c | 262 ++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 17 +-
lib/librte_eal/linuxapp/eal/eal_memory.c | 2 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 19 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 -------
lib/librte_malloc/malloc_elem.h | 190 ----
lib/librte_malloc/malloc_heap.c | 208 -----
lib/librte_malloc/malloc_heap.h | 70 --
lib/librte_malloc/rte_malloc.c | 228 +----
lib/librte_malloc/rte_malloc.h | 342 -------
lib/librte_malloc/rte_malloc_version.map | 16 -
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
58 files changed, 2988 insertions(+), 2371 deletions(-)
delete mode 100644 doc/guides/prog_guide/img/malloc_heap.png
create mode 100755 doc/guides/prog_guide/img/malloc_heap.svg
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 1/9] eal: move librte_malloc to eal/common
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
` (8 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
Move malloc inside eal and create a new section in MAINTAINERS file for
Memory Allocation in EAL.
Create a dummy malloc library to avoid breaking applications that have
librte_malloc in their DT_NEEDED entries.
This is the first step towards using malloc to allocate memory directly
from memsegs. Thus, memzones would allocate memory through malloc,
allowing to free memzones.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
MAINTAINERS | 22 +-
config/common_bsdapp | 9 +-
config/common_linuxapp | 9 +-
drivers/net/af_packet/Makefile | 1 -
drivers/net/bonding/Makefile | 1 -
drivers/net/e1000/Makefile | 2 +-
drivers/net/enic/Makefile | 2 +-
drivers/net/fm10k/Makefile | 2 +-
drivers/net/i40e/Makefile | 2 +-
drivers/net/ixgbe/Makefile | 2 +-
drivers/net/mlx4/Makefile | 1 -
drivers/net/null/Makefile | 1 -
drivers/net/pcap/Makefile | 1 -
drivers/net/virtio/Makefile | 2 +-
drivers/net/vmxnet3/Makefile | 2 +-
drivers/net/xenvirt/Makefile | 2 +-
lib/Makefile | 2 +-
lib/librte_acl/Makefile | 2 +-
lib/librte_eal/bsdapp/eal/Makefile | 4 +-
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 13 +
lib/librte_eal/common/Makefile | 1 +
lib/librte_eal/common/include/rte_malloc.h | 342 ++++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.c | 320 ++++++++++++++++++++++
lib/librte_eal/common/malloc_elem.h | 190 +++++++++++++
lib/librte_eal/common/malloc_heap.c | 208 ++++++++++++++
lib/librte_eal/common/malloc_heap.h | 70 +++++
lib/librte_eal/common/rte_malloc.c | 260 ++++++++++++++++++
lib/librte_eal/linuxapp/eal/Makefile | 4 +-
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 13 +
lib/librte_hash/Makefile | 2 +-
lib/librte_lpm/Makefile | 2 +-
lib/librte_malloc/Makefile | 6 +-
lib/librte_malloc/malloc_elem.c | 320 ----------------------
lib/librte_malloc/malloc_elem.h | 190 -------------
lib/librte_malloc/malloc_heap.c | 208 --------------
lib/librte_malloc/malloc_heap.h | 70 -----
lib/librte_malloc/rte_malloc.c | 228 +---------------
lib/librte_malloc/rte_malloc.h | 342 ------------------------
lib/librte_malloc/rte_malloc_version.map | 16 --
lib/librte_mempool/Makefile | 2 -
lib/librte_port/Makefile | 1 -
lib/librte_ring/Makefile | 3 +-
lib/librte_table/Makefile | 1 -
43 files changed, 1455 insertions(+), 1426 deletions(-)
create mode 100644 lib/librte_eal/common/include/rte_malloc.h
create mode 100644 lib/librte_eal/common/malloc_elem.c
create mode 100644 lib/librte_eal/common/malloc_elem.h
create mode 100644 lib/librte_eal/common/malloc_heap.c
create mode 100644 lib/librte_eal/common/malloc_heap.h
create mode 100644 lib/librte_eal/common/rte_malloc.c
delete mode 100644 lib/librte_malloc/malloc_elem.c
delete mode 100644 lib/librte_malloc/malloc_elem.h
delete mode 100644 lib/librte_malloc/malloc_heap.c
delete mode 100644 lib/librte_malloc/malloc_heap.h
delete mode 100644 lib/librte_malloc/rte_malloc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index a15105d..52892d0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -83,12 +83,9 @@ F: app/test/test_debug.c
F: app/test/test_devargs.c
F: app/test/test_eal*
F: app/test/test_errno.c
-F: app/test/test_func_reentrancy.c
F: app/test/test_interrupts.c
F: app/test/test_logs.c
F: app/test/test_memcpy*
-F: app/test/test_memory.c
-F: app/test/test_memzone.c
F: app/test/test_pci.c
F: app/test/test_per_lcore.c
F: app/test/test_prefetch.c
@@ -98,6 +95,19 @@ F: app/test/test_string_fns.c
F: app/test/test_tailq.c
F: app/test/test_version.c
+Memory Allocation
+M: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
+F: lib/librte_eal/common/include/rte_mem*
+F: lib/librte_eal/common/include/rte_malloc.h
+F: lib/librte_eal/common/*malloc*
+F: lib/librte_eal/common/eal_common_mem*
+F: lib/librte_eal/common/eal_hugepages.h
+F: doc/guides/prog_guide/malloc_lib.rst
+F: app/test/test_func_reentrancy.c
+F: app/test/test_malloc.c
+F: app/test/test_memory.c
+F: app/test/test_memzone.c
+
Secondary process
K: RTE_PROC_
F: doc/guides/prog_guide/multi_proc_support.rst
@@ -161,12 +171,6 @@ F: lib/librte_eal/bsdapp/nic_uio/
Core Libraries
--------------
-Dynamic memory
-F: lib/librte_malloc/
-F: doc/guides/prog_guide/malloc_lib.rst
-F: app/test/test_malloc.c
-F: app/test/test_func_reentrancy.c
-
Memory pool
M: Olivier Matz <olivier.matz@6wind.com>
F: lib/librte_mempool/
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 7112f1c..5bb7f55 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -106,6 +106,8 @@ CONFIG_RTE_LOG_LEVEL=8
CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
@@ -308,13 +310,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 4d90d35..7b57044 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -109,6 +109,8 @@ CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
@@ -316,13 +318,6 @@ CONFIG_RTE_LIBRTE_TIMER=y
CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
#
-# Compile librte_malloc
-#
-CONFIG_RTE_LIBRTE_MALLOC=y
-CONFIG_RTE_LIBRTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
-
-#
# Compile librte_cfgfile
#
CONFIG_RTE_LIBRTE_CFGFILE=y
diff --git a/drivers/net/af_packet/Makefile b/drivers/net/af_packet/Makefile
index f0bf537..ce5d239 100644
--- a/drivers/net/af_packet/Makefile
+++ b/drivers/net/af_packet/Makefile
@@ -58,7 +58,6 @@ SYMLINK-y-include += rte_eth_af_packet.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AF_PACKET) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 83ccce3..dee0875 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -61,7 +61,6 @@ SYMLINK-y-include += rte_eth_bond_8023ad.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 3d525fa..ccd2b7b 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -94,6 +94,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_EM_PMD) += em_rxtx.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/enic/Makefile b/drivers/net/enic/Makefile
index 52334c9..f0ee093 100644
--- a/drivers/net/enic/Makefile
+++ b/drivers/net/enic/Makefile
@@ -65,7 +65,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += base/vnic_rss.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_ENIC_PMD) += lib/librte_hash
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 7395933..a4a8f56 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -97,6 +97,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_api.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/i40e/Makefile b/drivers/net/i40e/Makefile
index 4fe371d..55b7d31 100644
--- a/drivers/net/i40e/Makefile
+++ b/drivers/net/i40e/Makefile
@@ -102,6 +102,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += i40e_fdir.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index f92a565..6095cc2 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -117,7 +117,7 @@ endif
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += lib/librte_net
ifeq ($(CONFIG_RTE_IXGBE_INC_VECTOR)$(CONFIG_RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC),yn)
$(error The ixgbe vpmd depends on Rx bulk alloc)
diff --git a/drivers/net/mlx4/Makefile b/drivers/net/mlx4/Makefile
index 725717f..14cb53f 100644
--- a/drivers/net/mlx4/Makefile
+++ b/drivers/net/mlx4/Makefile
@@ -42,7 +42,6 @@ DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MLX4_PMD) += lib/librte_malloc
# Basic CFLAGS.
CFLAGS += -O3
diff --git a/drivers/net/null/Makefile b/drivers/net/null/Makefile
index 6472015..96ba01c 100644
--- a/drivers/net/null/Makefile
+++ b/drivers/net/null/Makefile
@@ -56,7 +56,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_NULL) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/pcap/Makefile b/drivers/net/pcap/Makefile
index 0775dbc..48be913 100644
--- a/drivers/net/pcap/Makefile
+++ b/drivers/net/pcap/Makefile
@@ -57,7 +57,6 @@ SYMLINK-y-include +=
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_ether
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += lib/librte_kvargs
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 21ff7e5..930b60f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -55,6 +55,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile
index 901cee1..4cf3b33 100644
--- a/drivers/net/vmxnet3/Makefile
+++ b/drivers/net/vmxnet3/Makefile
@@ -79,6 +79,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += vmxnet3_ethdev.c
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += lib/librte_net
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/xenvirt/Makefile b/drivers/net/xenvirt/Makefile
index f0c796c..9c521d5 100644
--- a/drivers/net/xenvirt/Makefile
+++ b/drivers/net/xenvirt/Makefile
@@ -56,7 +56,7 @@ SYMLINK-y-include += rte_eth_xenvirt.h
# this lib depends upon:
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_eal lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_mempool lib/librte_mbuf
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_net
DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += lib/librte_cmdline
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/Makefile b/lib/Makefile
index 5f480f9..2055539 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -33,7 +33,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
DIRS-y += librte_compat
DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += librte_malloc
+DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_malloc
DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
diff --git a/lib/librte_acl/Makefile b/lib/librte_acl/Makefile
index 68dc248..46acc2b 100644
--- a/lib/librte_acl/Makefile
+++ b/lib/librte_acl/Makefile
@@ -75,6 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include := rte_acl_osdep.h
SYMLINK-$(CONFIG_RTE_LIBRTE_ACL)-include += rte_acl.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_ACL) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index 40ec648..064b0c5 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -40,7 +40,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
CFLAGS += -I$(RTE_SDK)/drivers/net/pcap
@@ -79,6 +78,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_BSDAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
#CFLAGS_eal_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 38772d4..0c43d6a 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -40,6 +40,7 @@ INC += rte_string_fns.h rte_version.h
INC += rte_eal_memconfig.h rte_malloc_heap.h
INC += rte_hexdump.h rte_devargs.h rte_dev.h
INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h
+INC += rte_malloc.h
ifeq ($(CONFIG_RTE_INSECURE_FUNCTION_WARNING),y)
INC += rte_warnings.h
diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
new file mode 100644
index 0000000..74bb78c
--- /dev/null
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -0,0 +1,342 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_MALLOC_H_
+#define _RTE_MALLOC_H_
+
+/**
+ * @file
+ * RTE Malloc. This library provides methods for dynamically allocating memory
+ * from hugepages.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
+ */
+struct rte_malloc_socket_stats {
+ size_t heap_totalsz_bytes; /**< Total bytes on heap */
+ size_t heap_freesz_bytes; /**< Total free bytes on heap */
+ size_t greatest_free_size; /**< Size in bytes of largest free block */
+ unsigned free_count; /**< Number of free elements on heap */
+ unsigned alloc_count; /**< Number of allocated elements on heap */
+ size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
+};
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared. In NUMA systems, the memory allocated resides on the same
+ * NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros. In NUMA systems, the memory allocated resides on the
+ * same NUMA socket as the core that calls this function.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align);
+
+/**
+ * Replacement function for realloc(), using huge-page memory. Reserved area
+ * memory is resized, preserving contents. In NUMA systems, the new area
+ * resides on the same NUMA socket as the old area.
+ *
+ * @param ptr
+ * Pointer to already allocated memory
+ * @param size
+ * Size (in bytes) of new area. If this is 0, memory is freed.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the reallocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align);
+
+/**
+ * This function allocates memory from the huge-page area of memory. The memory
+ * is not cleared.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_malloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Allocate zero'ed memory from the heap.
+ *
+ * Equivalent to rte_malloc() except that the memory zone is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param size
+ * Size (in bytes) to be allocated.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_zmalloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
+
+/**
+ * Replacement function for calloc(), using huge-page memory. Memory area is
+ * initialised with zeros.
+ *
+ * @param type
+ * A string identifying the type of allocated objects (useful for debug
+ * purposes, such as identifying the cause of a memory leak). Can be NULL.
+ * @param num
+ * Number of elements to be allocated.
+ * @param size
+ * Size (in bytes) of a single element.
+ * @param align
+ * If 0, the return is a pointer that is suitably aligned for any kind of
+ * variable (in the same manner as malloc()).
+ * Otherwise, the return is a pointer that is a multiple of *align*. In
+ * this case, it must obviously be a power of two. (Minimum alignment is the
+ * cacheline size, i.e. 64-bytes)
+ * @param socket
+ * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
+ * will behave the same as rte_calloc().
+ * @return
+ * - NULL on error. Not enough memory, or invalid arguments (size is 0,
+ * align is not a power of two).
+ * - Otherwise, the pointer to the allocated object.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
+
+/**
+ * Frees the memory space pointed to by the provided pointer.
+ *
+ * This pointer must have been returned by a previous call to
+ * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
+ * rte_free() is undefined if the pointer does not match this requirement.
+ *
+ * If the pointer is NULL, the function does nothing.
+ *
+ * @param ptr
+ * The pointer to memory to be freed.
+ */
+void
+rte_free(void *ptr);
+
+/**
+ * If malloc debug is enabled, check a memory block for header
+ * and trailer markers to indicate that all is well with the block.
+ * If size is non-null, also return the size of the block.
+ *
+ * @param ptr
+ * pointer to the start of a data block, must have been returned
+ * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
+ * or rte_realloc()
+ * @param size
+ * if non-null, and memory block pointer is valid, returns the size
+ * of the memory block
+ * @return
+ * -1 on error, invalid pointer passed or header and trailer markers
+ * are missing or corrupted
+ * 0 on success
+ */
+int
+rte_malloc_validate(const void *ptr, size_t *size);
+
+/**
+ * Get heap statistics for the specified heap.
+ *
+ * @param socket
+ * An unsigned integer specifying the socket to get heap statistics for
+ * @param socket_stats
+ * A structure which provides memory to store statistics
+ * @return
+ * Null on error
+ * Pointer to structure storing statistics on success
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats);
+
+/**
+ * Dump statistics.
+ *
+ * Dump for the specified type to the console. If the type argument is
+ * NULL, all memory types will be dumped.
+ *
+ * @param f
+ * A pointer to a file for output
+ * @param type
+ * A string identifying the type of objects to dump, or NULL
+ * to dump all objects.
+ */
+void
+rte_malloc_dump_stats(FILE *f, const char *type);
+
+/**
+ * Set the maximum amount of allocated memory for this type.
+ *
+ * This is not yet implemented
+ *
+ * @param type
+ * A string identifying the type of allocated objects.
+ * @param max
+ * The maximum amount of allocated bytes for this type.
+ * @return
+ * - 0: Success.
+ * - (-1): Error.
+ */
+int
+rte_malloc_set_limit(const char *type, size_t max);
+
+/**
+ * Return the physical address of a virtual address obtained through
+ * rte_malloc
+ *
+ * @param addr
+ * Adress obtained from a previous rte_malloc call
+ * @return
+ * NULL on error
+ * otherwise return physical address of the buffer
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
new file mode 100644
index 0000000..a5e1248
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -0,0 +1,320 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+
+/*
+ * initialise a general malloc_elem header structure
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+{
+ elem->heap = heap;
+ elem->mz = mz;
+ elem->prev = NULL;
+ memset(&elem->free_list, 0, sizeof(elem->free_list));
+ elem->state = ELEM_FREE;
+ elem->size = size;
+ elem->pad = 0;
+ set_header(elem);
+ set_trailer(elem);
+}
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
+{
+ malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ elem->prev = prev;
+ elem->state = ELEM_BUSY; /* mark busy so its never merged */
+}
+
+/*
+ * calculate the starting point of where data of the requested size
+ * and alignment would fit in the current element. If the data doesn't
+ * fit, return NULL.
+ */
+static void *
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ const uintptr_t end_pt = (uintptr_t)elem +
+ elem->size - MALLOC_ELEM_TRAILER_LEN;
+ const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+ /* if the new start point is before the exist start, it won't fit */
+ return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+}
+
+/*
+ * use elem_start_pt to determine if we get meet the size and
+ * alignment request from the current element
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ return elem_start_pt(elem, size, align) != NULL;
+}
+
+/*
+ * split an existing element into two smaller elements at the given
+ * split_pt parameter.
+ */
+static void
+split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
+{
+ struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
+ const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const unsigned new_elem_size = elem->size - old_elem_size;
+
+ malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ split_pt->prev = elem;
+ next_elem->prev = split_pt;
+ elem->size = old_elem_size;
+ set_trailer(elem);
+}
+
+/*
+ * Given an element size, compute its freelist index.
+ * We free an element into the freelist containing similarly-sized elements.
+ * We try to allocate elements starting with the freelist containing
+ * similarly-sized elements, and if necessary, we search freelists
+ * containing larger elements.
+ *
+ * Example element size ranges for a heap with five free lists:
+ * heap->free_head[0] - (0 , 2^8]
+ * heap->free_head[1] - (2^8 , 2^10]
+ * heap->free_head[2] - (2^10 ,2^12]
+ * heap->free_head[3] - (2^12, 2^14]
+ * heap->free_head[4] - (2^14, MAX_SIZE]
+ */
+size_t
+malloc_elem_free_list_index(size_t size)
+{
+#define MALLOC_MINSIZE_LOG2 8
+#define MALLOC_LOG2_INCREMENT 2
+
+ size_t log2;
+ size_t index;
+
+ if (size <= (1UL << MALLOC_MINSIZE_LOG2))
+ return 0;
+
+ /* Find next power of 2 >= size. */
+ log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
+
+ /* Compute freelist index, based on log2(size). */
+ index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
+ MALLOC_LOG2_INCREMENT;
+
+ return (index <= RTE_HEAP_NUM_FREELISTS-1?
+ index: RTE_HEAP_NUM_FREELISTS-1);
+}
+
+/*
+ * Add the specified element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem)
+{
+ size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+
+ elem->state = ELEM_FREE;
+ LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
+}
+
+/*
+ * Remove the specified element from its heap's free list.
+ */
+static void
+elem_free_list_remove(struct malloc_elem *elem)
+{
+ LIST_REMOVE(elem, free_list);
+}
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ * This function is only called from malloc_heap_alloc so parameter checking
+ * is not done here, as it's done there previously.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+{
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
+ const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ /* don't split it, pad the element instead */
+ elem->state = ELEM_BUSY;
+ elem->pad = old_elem_size;
+
+ /* put a dummy header in padding, to point to real element header */
+ if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
+ * is cache-line aligned */
+ new_elem->pad = elem->pad;
+ new_elem->state = ELEM_PAD;
+ new_elem->size = elem->size - elem->pad;
+ set_header(new_elem);
+ }
+ /* remove element from free list */
+ elem_free_list_remove(elem);
+
+ return new_elem;
+ }
+
+ /* we are going to split the element in two. The original element
+ * remains free, and the new element is the one allocated.
+ * Re-insert original element, in case its new size makes it
+ * belong on a different list.
+ */
+ elem_free_list_remove(elem);
+ split_elem(elem, new_elem);
+ new_elem->state = ELEM_BUSY;
+ malloc_elem_free_list_insert(elem);
+
+ return new_elem;
+}
+
+/*
+ * joing two struct malloc_elem together. elem1 and elem2 must
+ * be contiguous in memory.
+ */
+static inline void
+join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
+{
+ struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
+ elem1->size += elem2->size;
+ next->prev = elem1;
+}
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem)
+{
+ if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
+ return -1;
+
+ rte_spinlock_lock(&(elem->heap->lock));
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ if (next->state == ELEM_FREE){
+ /* remove from free list, join to this one */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+ }
+
+ /* check if previous element is free, if so join with it and return,
+ * need to re-insert in free list, as that element's size is changing
+ */
+ if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
+ elem_free_list_remove(elem->prev);
+ join_elem(elem->prev, elem);
+ malloc_elem_free_list_insert(elem->prev);
+ }
+ /* otherwise add ourselves to the free list */
+ else {
+ malloc_elem_free_list_insert(elem);
+ elem->pad = 0;
+ }
+ /* decrease heap's count of allocated elements */
+ elem->heap->alloc_count--;
+ rte_spinlock_unlock(&(elem->heap->lock));
+
+ return 0;
+}
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size)
+{
+ const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
+ /* if we request a smaller size, then always return ok */
+ const size_t current_size = elem->size - elem->pad;
+ if (current_size >= new_size)
+ return 0;
+
+ struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
+ rte_spinlock_lock(&elem->heap->lock);
+ if (next ->state != ELEM_FREE)
+ goto err_return;
+ if (current_size + next->size < new_size)
+ goto err_return;
+
+ /* we now know the element fits, so remove from free list,
+ * join the two
+ */
+ elem_free_list_remove(next);
+ join_elem(elem, next);
+
+ if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
+ /* now we have a big block together. Lets cut it down a bit, by splitting */
+ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
+ split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
+ split_elem(elem, split_pt);
+ malloc_elem_free_list_insert(split_pt);
+ }
+ rte_spinlock_unlock(&elem->heap->lock);
+ return 0;
+
+err_return:
+ rte_spinlock_unlock(&elem->heap->lock);
+ return -1;
+}
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
new file mode 100644
index 0000000..9790b1a
--- /dev/null
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -0,0 +1,190 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_ELEM_H_
+#define MALLOC_ELEM_H_
+
+#include <rte_memory.h>
+
+/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
+struct malloc_heap;
+
+enum elem_state {
+ ELEM_FREE = 0,
+ ELEM_BUSY,
+ ELEM_PAD /* element is a padding-only header */
+};
+
+struct malloc_elem {
+ struct malloc_heap *heap;
+ struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
+ const struct rte_memzone *mz;
+ volatile enum elem_state state;
+ uint32_t pad;
+ size_t size;
+#ifdef RTE_LIBRTE_MALLOC_DEBUG
+ uint64_t header_cookie; /* Cookie marking start of data */
+ /* trailer cookie at start + size */
+#endif
+} __rte_cache_aligned;
+
+#ifndef RTE_LIBRTE_MALLOC_DEBUG
+static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
+
+/* dummy function - just check if pointer is non-null */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
+
+/* dummy function - no header if malloc_debug is not enabled */
+static inline void
+set_header(struct malloc_elem *elem __rte_unused){ }
+
+/* dummy function - no trailer if malloc_debug is not enabled */
+static inline void
+set_trailer(struct malloc_elem *elem __rte_unused){ }
+
+
+#else
+static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
+
+#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
+#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
+
+/* define macros to make referencing the header and trailer cookies easier */
+#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
+ elem->size - MALLOC_ELEM_TRAILER_LEN)))
+#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
+
+static inline void
+set_header(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
+}
+
+static inline void
+set_trailer(struct malloc_elem *elem)
+{
+ if (elem != NULL)
+ MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
+}
+
+/* check that the header and trailer cookies are set correctly */
+static inline int
+malloc_elem_cookies_ok(const struct malloc_elem *elem)
+{
+ return (elem != NULL &&
+ MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
+ MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
+}
+
+#endif
+
+static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
+#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
+
+/*
+ * Given a pointer to the start of a memory block returned by malloc, get
+ * the actual malloc_elem header for that block.
+ */
+static inline struct malloc_elem *
+malloc_elem_from_data(const void *data)
+{
+ if (data == NULL)
+ return NULL;
+
+ struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
+ if (!malloc_elem_cookies_ok(elem))
+ return NULL;
+ return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
+}
+
+/*
+ * initialise a malloc_elem header
+ */
+void
+malloc_elem_init(struct malloc_elem *elem,
+ struct malloc_heap *heap,
+ const struct rte_memzone *mz,
+ size_t size);
+
+/*
+ * initialise a dummy malloc_elem header for the end-of-memzone marker
+ */
+void
+malloc_elem_mkend(struct malloc_elem *elem,
+ struct malloc_elem *prev_free);
+
+/*
+ * return true if the current malloc_elem can hold a block of data
+ * of the requested size and with the requested alignment
+ */
+int
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * reserve a block of data in an existing malloc_elem. If the malloc_elem
+ * is much larger than the data block requested, we split the element in two.
+ */
+struct malloc_elem *
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+
+/*
+ * free a malloc_elem block by adding it to the free list. If the
+ * blocks either immediately before or immediately after newly freed block
+ * are also free, the blocks are merged together.
+ */
+int
+malloc_elem_free(struct malloc_elem *elem);
+
+/*
+ * attempt to resize a malloc_elem by expanding into any free space
+ * immediately after it in memory.
+ */
+int
+malloc_elem_resize(struct malloc_elem *elem, size_t size);
+
+/*
+ * Given an element size, compute its freelist index.
+ */
+size_t
+malloc_elem_free_list_index(size_t size);
+
+/*
+ * Add element to its heap's free list.
+ */
+void
+malloc_elem_free_list_insert(struct malloc_elem *elem);
+
+#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
new file mode 100644
index 0000000..8861d27
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -0,0 +1,208 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_spinlock.h>
+#include <rte_memcpy.h>
+#include <rte_atomic.h>
+
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+/* since the memzone size starts with a digit, it will appear unquoted in
+ * rte_config.h, so quote it so it can be passed to rte_str_to_size */
+#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
+
+/*
+ * returns the configuration setting for the memzone size as a size_t value
+ */
+static inline size_t
+get_malloc_memzone_size(void)
+{
+ return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+}
+
+/*
+ * reserve an extra memory zone and make it available for use by a particular
+ * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * to prevent overflow. The rest of the zone is added to free list as a single
+ * large free block
+ */
+static int
+malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ const unsigned mz_flags = 0;
+ const size_t block_size = get_malloc_memzone_size();
+ /* ensure the data we want to allocate will fit in the memzone */
+ const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
+ const struct rte_memzone *mz = NULL;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned numa_socket = heap - mcfg->malloc_heaps;
+
+ size_t mz_size = min_size;
+ if (mz_size < block_size)
+ mz_size = block_size;
+
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
+ numa_socket, heap->mz_count++);
+
+ /* try getting a block. if we fail and we don't need as big a block
+ * as given in the config, we can shrink our request and try again
+ */
+ do {
+ mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
+ mz_flags);
+ if (mz == NULL)
+ mz_size /= 2;
+ } while (mz == NULL && mz_size > min_size);
+ if (mz == NULL)
+ return -1;
+
+ /* allocate the memory block headers, one at end, one at start */
+ struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
+ mz_size - MALLOC_ELEM_OVERHEAD);
+ end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+
+ const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
+ malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_mkend(end_elem, start_elem);
+ malloc_elem_free_list_insert(start_elem);
+
+ /* increase heap total size by size of new memzone */
+ heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Iterates through the freelist for a heap to find a free element
+ * which can store data of the required size and with the requested alignment.
+ * Returns null on failure, or pointer to element on success.
+ */
+static struct malloc_elem *
+find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ for (idx = malloc_elem_free_list_index(size);
+ idx < RTE_HEAP_NUM_FREELISTS; idx++)
+ {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ if (malloc_elem_can_hold(elem, size, align))
+ return elem;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Main function called by malloc to allocate a block of memory from the
+ * heap. It locks the free list, scans it, and adds a new memzone if the
+ * scan fails. Once the new memzone is added, it re-scans and should return
+ * the new element after releasing the lock.
+ */
+void *
+malloc_heap_alloc(struct malloc_heap *heap,
+ const char *type __attribute__((unused)), size_t size, unsigned align)
+{
+ size = RTE_CACHE_LINE_ROUNDUP(size);
+ align = RTE_CACHE_LINE_ROUNDUP(align);
+ rte_spinlock_lock(&heap->lock);
+ struct malloc_elem *elem = find_suitable_element(heap, size, align);
+ if (elem == NULL){
+ if ((malloc_heap_add_memzone(heap, size, align)) == 0)
+ elem = find_suitable_element(heap, size, align);
+ }
+
+ if (elem != NULL){
+ elem = malloc_elem_alloc(elem, size, align);
+ /* increase heap's count of allocated elements */
+ heap->alloc_count++;
+ }
+ rte_spinlock_unlock(&heap->lock);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
+
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ size_t idx;
+ struct malloc_elem *elem;
+
+ /* Initialise variables for heap */
+ socket_stats->free_count = 0;
+ socket_stats->heap_freesz_bytes = 0;
+ socket_stats->greatest_free_size = 0;
+
+ /* Iterate through free list */
+ for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
+ for (elem = LIST_FIRST(&heap->free_head[idx]);
+ !!elem; elem = LIST_NEXT(elem, free_list))
+ {
+ socket_stats->free_count++;
+ socket_stats->heap_freesz_bytes += elem->size;
+ if (elem->size > socket_stats->greatest_free_size)
+ socket_stats->greatest_free_size = elem->size;
+ }
+ }
+ /* Get stats on overall heap and allocated memory on this heap */
+ socket_stats->heap_totalsz_bytes = heap->total_size;
+ socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
+ socket_stats->heap_freesz_bytes);
+ socket_stats->alloc_count = heap->alloc_count;
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
new file mode 100644
index 0000000..a47136d
--- /dev/null
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_HEAP_H_
+#define MALLOC_HEAP_H_
+
+#include <rte_malloc.h>
+#include <rte_malloc_heap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline unsigned
+malloc_get_numa_socket(void)
+{
+ unsigned socket_id = rte_socket_id();
+
+ if (socket_id == (unsigned)SOCKET_ID_ANY)
+ return 0;
+
+ return socket_id;
+}
+
+void *
+malloc_heap_alloc(struct malloc_heap *heap, const char *type,
+ size_t size, unsigned align);
+
+int
+malloc_heap_get_stats(const struct malloc_heap *heap,
+ struct rte_malloc_socket_stats *socket_stats);
+
+int
+rte_eal_heap_memzone_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
new file mode 100644
index 0000000..c313a57
--- /dev/null
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -0,0 +1,260 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_spinlock.h>
+
+#include <rte_malloc.h>
+#include "malloc_elem.h"
+#include "malloc_heap.h"
+
+
+/* Free the memory space back to heap */
+void rte_free(void *addr)
+{
+ if (addr == NULL) return;
+ if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
+ rte_panic("Fatal error: Invalid memory\n");
+}
+
+/*
+ * Allocate memory on specified heap.
+ */
+void *
+rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ int socket, i;
+ void *ret;
+
+ /* return NULL if size is 0 or alignment is not power-of-2 */
+ if (size == 0 || (align && !rte_is_power_of_2(align)))
+ return NULL;
+
+ if (socket_arg == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_arg;
+
+ /* Check socket parameter */
+ if (socket >= RTE_MAX_NUMA_NODES)
+ return NULL;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL || socket_arg != SOCKET_ID_ANY)
+ return ret;
+
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ /* we already tried this one */
+ if (i == socket)
+ continue;
+
+ ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
+ size, align == 0 ? 1 : align);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate memory on default heap.
+ */
+void *
+rte_malloc(const char *type, size_t size, unsigned align)
+{
+ return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
+{
+ void *ptr = rte_malloc_socket(type, size, align, socket);
+
+ if (ptr != NULL)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_zmalloc(const char *type, size_t size, unsigned align)
+{
+ return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
+}
+
+/*
+ * Allocate zero'd memory on specified heap.
+ */
+void *
+rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
+{
+ return rte_zmalloc_socket(type, num * size, align, socket);
+}
+
+/*
+ * Allocate zero'd memory on default heap.
+ */
+void *
+rte_calloc(const char *type, size_t num, size_t size, unsigned align)
+{
+ return rte_zmalloc(type, num * size, align);
+}
+
+/*
+ * Resize allocated memory.
+ */
+void *
+rte_realloc(void *ptr, size_t size, unsigned align)
+{
+ if (ptr == NULL)
+ return rte_malloc(NULL, size, align);
+
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (elem == NULL)
+ rte_panic("Fatal error: memory corruption detected\n");
+
+ size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
+ /* check alignment matches first, and if ok, see if we can resize block */
+ if (RTE_PTR_ALIGN(ptr,align) == ptr &&
+ malloc_elem_resize(elem, size) == 0)
+ return ptr;
+
+ /* either alignment is off, or we have no room to expand,
+ * so move data. */
+ void *new_ptr = rte_malloc(NULL, size, align);
+ if (new_ptr == NULL)
+ return NULL;
+ const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
+ rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
+ rte_free(ptr);
+
+ return new_ptr;
+}
+
+int
+rte_malloc_validate(const void *ptr, size_t *size)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ if (!malloc_elem_cookies_ok(elem))
+ return -1;
+ if (size != NULL)
+ *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
+ return 0;
+}
+
+/*
+ * Function to retrieve data for heap on given socket
+ */
+int
+rte_malloc_get_socket_stats(int socket,
+ struct rte_malloc_socket_stats *socket_stats)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
+ return -1;
+
+ return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
+}
+
+/*
+ * Print stats on memory type. If type is NULL, info on all types is printed
+ */
+void
+rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
+{
+ unsigned int socket;
+ struct rte_malloc_socket_stats sock_stats;
+ /* Iterate through all initialised heaps */
+ for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
+ if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
+ continue;
+
+ fprintf(f, "Socket:%u\n", socket);
+ fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
+ fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
+ fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
+ fprintf(f, "\tGreatest_free_size:%zu,\n",
+ sock_stats.greatest_free_size);
+ fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
+ fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
+ }
+ return;
+}
+
+/*
+ * TODO: Set limit to memory that can be allocated to memory type
+ */
+int
+rte_malloc_set_limit(__rte_unused const char *type,
+ __rte_unused size_t max)
+{
+ return 0;
+}
+
+/*
+ * Return the physical address of a virtual address obtained through rte_malloc
+ */
+phys_addr_t
+rte_malloc_virt2phy(const void *addr)
+{
+ const struct malloc_elem *elem = malloc_elem_from_data(addr);
+ if (elem == NULL)
+ return 0;
+ return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+}
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 42a16fe..00ed62e 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -44,7 +44,6 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common
CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
CFLAGS += -I$(RTE_SDK)/lib/librte_ring
CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
-CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
CFLAGS += -I$(RTE_SDK)/lib/librte_ether
CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
CFLAGS += -I$(RTE_SDK)/drivers/net/ring
@@ -91,6 +90,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_devargs.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_options.c
SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_thread.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += rte_malloc.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_elem.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += malloc_heap.c
CFLAGS_eal.o := -D_GNU_SOURCE
CFLAGS_eal_interrupts.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 7e850a9..c107b05 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -10,6 +10,8 @@ DPDK_2.0 {
pci_driver_list;
per_lcore__lcore_id;
per_lcore__rte_errno;
+ rte_calloc;
+ rte_calloc_socket;
rte_cpu_check_supported;
rte_cpu_get_flag_enabled;
rte_cycles_vmware_tsc_map;
@@ -53,6 +55,7 @@ DPDK_2.0 {
rte_eal_vdev_uninit;
rte_eal_wait_lcore;
rte_exit;
+ rte_free;
rte_get_hpet_cycles;
rte_get_hpet_hz;
rte_get_tsc_hz;
@@ -68,6 +71,13 @@ DPDK_2.0 {
rte_log_dump_history;
rte_log_set_history;
rte_logs;
+ rte_malloc;
+ rte_malloc_dump_stats;
+ rte_malloc_get_socket_stats;
+ rte_malloc_set_limit;
+ rte_malloc_socket;
+ rte_malloc_validate;
+ rte_malloc_virt2phy;
rte_mem_lock_page;
rte_mem_phy2mch;
rte_mem_virt2phy;
@@ -81,6 +91,7 @@ DPDK_2.0 {
rte_memzone_reserve_bounded;
rte_memzone_walk;
rte_openlog_stream;
+ rte_realloc;
rte_set_application_usage_hook;
rte_set_log_level;
rte_set_log_type;
@@ -94,6 +105,8 @@ DPDK_2.0 {
rte_vlog;
rte_xen_dom0_memory_attach;
rte_xen_dom0_memory_init;
+ rte_zmalloc;
+ rte_zmalloc_socket;
test_mp_secondary;
local: *;
diff --git a/lib/librte_hash/Makefile b/lib/librte_hash/Makefile
index 039da46..4bb3848 100644
--- a/lib/librte_hash/Makefile
+++ b/lib/librte_hash/Makefile
@@ -53,6 +53,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_thash.h
SYMLINK-$(CONFIG_RTE_LIBRTE_HASH)-include += rte_fbk_hash.h
# this lib needs eal and ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_malloc lib/librte_ring
+DEPDIRS-$(CONFIG_RTE_LIBRTE_HASH) += lib/librte_eal lib/librte_ring
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_lpm/Makefile b/lib/librte_lpm/Makefile
index 35e6389..0a7a888 100644
--- a/lib/librte_lpm/Makefile
+++ b/lib/librte_lpm/Makefile
@@ -48,6 +48,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_LPM) := rte_lpm.c rte_lpm6.c
SYMLINK-$(CONFIG_RTE_LIBRTE_LPM)-include := rte_lpm.h rte_lpm6.h
# this lib needs eal
-DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LPM) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_malloc/Makefile b/lib/librte_malloc/Makefile
index 947e41c..32d86b9 100644
--- a/lib/librte_malloc/Makefile
+++ b/lib/librte_malloc/Makefile
@@ -28,7 +28,6 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
include $(RTE_SDK)/mk/rte.vars.mk
# library name
@@ -41,10 +40,7 @@ CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
EXPORT_MAP := rte_malloc_version.map
# all source are stored in SRCS-y
-SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c malloc_elem.c malloc_heap.c
-
-# install includes
-SYMLINK-$(CONFIG_RTE_LIBRTE_MALLOC)-include := rte_malloc.h
+SRCS-$(CONFIG_RTE_LIBRTE_MALLOC) := rte_malloc.c
# this lib needs eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_MALLOC) += lib/librte_eal
diff --git a/lib/librte_malloc/malloc_elem.c b/lib/librte_malloc/malloc_elem.c
deleted file mode 100644
index a5e1248..0000000
--- a/lib/librte_malloc/malloc_elem.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
-
-/*
- * initialise a general malloc_elem header structure
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
-{
- elem->heap = heap;
- elem->mz = mz;
- elem->prev = NULL;
- memset(&elem->free_list, 0, sizeof(elem->free_list));
- elem->state = ELEM_FREE;
- elem->size = size;
- elem->pad = 0;
- set_header(elem);
- set_trailer(elem);
-}
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
-{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
- elem->prev = prev;
- elem->state = ELEM_BUSY; /* mark busy so its never merged */
-}
-
-/*
- * calculate the starting point of where data of the requested size
- * and alignment would fit in the current element. If the data doesn't
- * fit, return NULL.
- */
-static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
-{
- const uintptr_t end_pt = (uintptr_t)elem +
- elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
-
- /* if the new start point is before the exist start, it won't fit */
- return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
-}
-
-/*
- * use elem_start_pt to determine if we get meet the size and
- * alignment request from the current element
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
-{
- return elem_start_pt(elem, size, align) != NULL;
-}
-
-/*
- * split an existing element into two smaller elements at the given
- * split_pt parameter.
- */
-static void
-split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
-{
- struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
-
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
- split_pt->prev = elem;
- next_elem->prev = split_pt;
- elem->size = old_elem_size;
- set_trailer(elem);
-}
-
-/*
- * Given an element size, compute its freelist index.
- * We free an element into the freelist containing similarly-sized elements.
- * We try to allocate elements starting with the freelist containing
- * similarly-sized elements, and if necessary, we search freelists
- * containing larger elements.
- *
- * Example element size ranges for a heap with five free lists:
- * heap->free_head[0] - (0 , 2^8]
- * heap->free_head[1] - (2^8 , 2^10]
- * heap->free_head[2] - (2^10 ,2^12]
- * heap->free_head[3] - (2^12, 2^14]
- * heap->free_head[4] - (2^14, MAX_SIZE]
- */
-size_t
-malloc_elem_free_list_index(size_t size)
-{
-#define MALLOC_MINSIZE_LOG2 8
-#define MALLOC_LOG2_INCREMENT 2
-
- size_t log2;
- size_t index;
-
- if (size <= (1UL << MALLOC_MINSIZE_LOG2))
- return 0;
-
- /* Find next power of 2 >= size. */
- log2 = sizeof(size) * 8 - __builtin_clzl(size-1);
-
- /* Compute freelist index, based on log2(size). */
- index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
- MALLOC_LOG2_INCREMENT;
-
- return (index <= RTE_HEAP_NUM_FREELISTS-1?
- index: RTE_HEAP_NUM_FREELISTS-1);
-}
-
-/*
- * Add the specified element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem)
-{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
-
- elem->state = ELEM_FREE;
- LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
-}
-
-/*
- * Remove the specified element from its heap's free list.
- */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
-{
- LIST_REMOVE(elem, free_list);
-}
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- * This function is only called from malloc_heap_alloc so parameter checking
- * is not done here, as it's done there previously.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
-{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
-
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
- /* don't split it, pad the element instead */
- elem->state = ELEM_BUSY;
- elem->pad = old_elem_size;
-
- /* put a dummy header in padding, to point to real element header */
- if (elem->pad > 0){ /* pad will be at least 64-bytes, as everything
- * is cache-line aligned */
- new_elem->pad = elem->pad;
- new_elem->state = ELEM_PAD;
- new_elem->size = elem->size - elem->pad;
- set_header(new_elem);
- }
- /* remove element from free list */
- elem_free_list_remove(elem);
-
- return new_elem;
- }
-
- /* we are going to split the element in two. The original element
- * remains free, and the new element is the one allocated.
- * Re-insert original element, in case its new size makes it
- * belong on a different list.
- */
- elem_free_list_remove(elem);
- split_elem(elem, new_elem);
- new_elem->state = ELEM_BUSY;
- malloc_elem_free_list_insert(elem);
-
- return new_elem;
-}
-
-/*
- * joing two struct malloc_elem together. elem1 and elem2 must
- * be contiguous in memory.
- */
-static inline void
-join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
-{
- struct malloc_elem *next = RTE_PTR_ADD(elem2, elem2->size);
- elem1->size += elem2->size;
- next->prev = elem1;
-}
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem)
-{
- if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
- return -1;
-
- rte_spinlock_lock(&(elem->heap->lock));
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- if (next->state == ELEM_FREE){
- /* remove from free list, join to this one */
- elem_free_list_remove(next);
- join_elem(elem, next);
- }
-
- /* check if previous element is free, if so join with it and return,
- * need to re-insert in free list, as that element's size is changing
- */
- if (elem->prev != NULL && elem->prev->state == ELEM_FREE) {
- elem_free_list_remove(elem->prev);
- join_elem(elem->prev, elem);
- malloc_elem_free_list_insert(elem->prev);
- }
- /* otherwise add ourselves to the free list */
- else {
- malloc_elem_free_list_insert(elem);
- elem->pad = 0;
- }
- /* decrease heap's count of allocated elements */
- elem->heap->alloc_count--;
- rte_spinlock_unlock(&(elem->heap->lock));
-
- return 0;
-}
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size)
-{
- const size_t new_size = size + MALLOC_ELEM_OVERHEAD;
- /* if we request a smaller size, then always return ok */
- const size_t current_size = elem->size - elem->pad;
- if (current_size >= new_size)
- return 0;
-
- struct malloc_elem *next = RTE_PTR_ADD(elem, elem->size);
- rte_spinlock_lock(&elem->heap->lock);
- if (next ->state != ELEM_FREE)
- goto err_return;
- if (current_size + next->size < new_size)
- goto err_return;
-
- /* we now know the element fits, so remove from free list,
- * join the two
- */
- elem_free_list_remove(next);
- join_elem(elem, next);
-
- if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD){
- /* now we have a big block together. Lets cut it down a bit, by splitting */
- struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
- split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
- split_elem(elem, split_pt);
- malloc_elem_free_list_insert(split_pt);
- }
- rte_spinlock_unlock(&elem->heap->lock);
- return 0;
-
-err_return:
- rte_spinlock_unlock(&elem->heap->lock);
- return -1;
-}
diff --git a/lib/librte_malloc/malloc_elem.h b/lib/librte_malloc/malloc_elem.h
deleted file mode 100644
index 9790b1a..0000000
--- a/lib/librte_malloc/malloc_elem.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_ELEM_H_
-#define MALLOC_ELEM_H_
-
-#include <rte_memory.h>
-
-/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
-struct malloc_heap;
-
-enum elem_state {
- ELEM_FREE = 0,
- ELEM_BUSY,
- ELEM_PAD /* element is a padding-only header */
-};
-
-struct malloc_elem {
- struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
- LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
- volatile enum elem_state state;
- uint32_t pad;
- size_t size;
-#ifdef RTE_LIBRTE_MALLOC_DEBUG
- uint64_t header_cookie; /* Cookie marking start of data */
- /* trailer cookie at start + size */
-#endif
-} __rte_cache_aligned;
-
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
-static const unsigned MALLOC_ELEM_TRAILER_LEN = 0;
-
-/* dummy function - just check if pointer is non-null */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem){ return elem != NULL; }
-
-/* dummy function - no header if malloc_debug is not enabled */
-static inline void
-set_header(struct malloc_elem *elem __rte_unused){ }
-
-/* dummy function - no trailer if malloc_debug is not enabled */
-static inline void
-set_trailer(struct malloc_elem *elem __rte_unused){ }
-
-
-#else
-static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE;
-
-#define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */
-#define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/
-
-/* define macros to make referencing the header and trailer cookies easier */
-#define MALLOC_ELEM_TRAILER(elem) (*((uint64_t*)RTE_PTR_ADD(elem, \
- elem->size - MALLOC_ELEM_TRAILER_LEN)))
-#define MALLOC_ELEM_HEADER(elem) (elem->header_cookie)
-
-static inline void
-set_header(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_HEADER(elem) = MALLOC_HEADER_COOKIE;
-}
-
-static inline void
-set_trailer(struct malloc_elem *elem)
-{
- if (elem != NULL)
- MALLOC_ELEM_TRAILER(elem) = MALLOC_TRAILER_COOKIE;
-}
-
-/* check that the header and trailer cookies are set correctly */
-static inline int
-malloc_elem_cookies_ok(const struct malloc_elem *elem)
-{
- return (elem != NULL &&
- MALLOC_ELEM_HEADER(elem) == MALLOC_HEADER_COOKIE &&
- MALLOC_ELEM_TRAILER(elem) == MALLOC_TRAILER_COOKIE);
-}
-
-#endif
-
-static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem);
-#define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN)
-
-/*
- * Given a pointer to the start of a memory block returned by malloc, get
- * the actual malloc_elem header for that block.
- */
-static inline struct malloc_elem *
-malloc_elem_from_data(const void *data)
-{
- if (data == NULL)
- return NULL;
-
- struct malloc_elem *elem = RTE_PTR_SUB(data, MALLOC_ELEM_HEADER_LEN);
- if (!malloc_elem_cookies_ok(elem))
- return NULL;
- return elem->state != ELEM_PAD ? elem: RTE_PTR_SUB(elem, elem->pad);
-}
-
-/*
- * initialise a malloc_elem header
- */
-void
-malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap,
- const struct rte_memzone *mz,
- size_t size);
-
-/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
- */
-void
-malloc_elem_mkend(struct malloc_elem *elem,
- struct malloc_elem *prev_free);
-
-/*
- * return true if the current malloc_elem can hold a block of data
- * of the requested size and with the requested alignment
- */
-int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * reserve a block of data in an existing malloc_elem. If the malloc_elem
- * is much larger than the data block requested, we split the element in two.
- */
-struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
-
-/*
- * free a malloc_elem block by adding it to the free list. If the
- * blocks either immediately before or immediately after newly freed block
- * are also free, the blocks are merged together.
- */
-int
-malloc_elem_free(struct malloc_elem *elem);
-
-/*
- * attempt to resize a malloc_elem by expanding into any free space
- * immediately after it in memory.
- */
-int
-malloc_elem_resize(struct malloc_elem *elem, size_t size);
-
-/*
- * Given an element size, compute its freelist index.
- */
-size_t
-malloc_elem_free_list_index(size_t size);
-
-/*
- * Add element to its heap's free list.
- */
-void
-malloc_elem_free_list_insert(struct malloc_elem *elem);
-
-#endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_malloc/malloc_heap.c b/lib/librte_malloc/malloc_heap.c
deleted file mode 100644
index 8861d27..0000000
--- a/lib/librte_malloc/malloc_heap.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_string_fns.h>
-#include <rte_spinlock.h>
-#include <rte_memcpy.h>
-#include <rte_atomic.h>
-
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
-{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
-}
-
-/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
- * to prevent overflow. The rest of the zone is added to free list as a single
- * large free block
- */
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
-{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
- /* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
- end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
-
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
- malloc_elem_mkend(end_elem, start_elem);
- malloc_elem_free_list_insert(start_elem);
-
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Iterates through the freelist for a heap to find a free element
- * which can store data of the required size and with the requested alignment.
- * Returns null on failure, or pointer to element on success.
- */
-static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
- }
- }
- return NULL;
-}
-
-/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
- * the new element after releasing the lock.
- */
-void *
-malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
-{
- size = RTE_CACHE_LINE_ROUNDUP(size);
- align = RTE_CACHE_LINE_ROUNDUP(align);
- rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
-
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
- /* increase heap's count of allocated elements */
- heap->alloc_count++;
- }
- rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
-
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats)
-{
- size_t idx;
- struct malloc_elem *elem;
-
- /* Initialise variables for heap */
- socket_stats->free_count = 0;
- socket_stats->heap_freesz_bytes = 0;
- socket_stats->greatest_free_size = 0;
-
- /* Iterate through free list */
- for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
- for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- socket_stats->free_count++;
- socket_stats->heap_freesz_bytes += elem->size;
- if (elem->size > socket_stats->greatest_free_size)
- socket_stats->greatest_free_size = elem->size;
- }
- }
- /* Get stats on overall heap and allocated memory on this heap */
- socket_stats->heap_totalsz_bytes = heap->total_size;
- socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
- socket_stats->heap_freesz_bytes);
- socket_stats->alloc_count = heap->alloc_count;
- return 0;
-}
diff --git a/lib/librte_malloc/malloc_heap.h b/lib/librte_malloc/malloc_heap.h
deleted file mode 100644
index a47136d..0000000
--- a/lib/librte_malloc/malloc_heap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MALLOC_HEAP_H_
-#define MALLOC_HEAP_H_
-
-#include <rte_malloc.h>
-#include <rte_malloc_heap.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static inline unsigned
-malloc_get_numa_socket(void)
-{
- unsigned socket_id = rte_socket_id();
-
- if (socket_id == (unsigned)SOCKET_ID_ANY)
- return 0;
-
- return socket_id;
-}
-
-void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
-
-int
-malloc_heap_get_stats(const struct malloc_heap *heap,
- struct rte_malloc_socket_stats *socket_stats);
-
-int
-rte_eal_heap_memzone_init(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MALLOC_HEAP_H_ */
diff --git a/lib/librte_malloc/rte_malloc.c b/lib/librte_malloc/rte_malloc.c
index c313a57..4b9dc7f 100644
--- a/lib/librte_malloc/rte_malloc.c
+++ b/lib/librte_malloc/rte_malloc.c
@@ -31,230 +31,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-
-#include <rte_memcpy.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_eal_memconfig.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_common.h>
-#include <rte_spinlock.h>
-
-#include <rte_malloc.h>
-#include "malloc_elem.h"
-#include "malloc_heap.h"
-
-
-/* Free the memory space back to heap */
-void rte_free(void *addr)
-{
- if (addr == NULL) return;
- if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
- rte_panic("Fatal error: Invalid memory\n");
-}
-
-/*
- * Allocate memory on specified heap.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- int socket, i;
- void *ret;
-
- /* return NULL if size is 0 or alignment is not power-of-2 */
- if (size == 0 || (align && !rte_is_power_of_2(align)))
- return NULL;
-
- if (socket_arg == SOCKET_ID_ANY)
- socket = malloc_get_numa_socket();
- else
- socket = socket_arg;
-
- /* Check socket parameter */
- if (socket >= RTE_MAX_NUMA_NODES)
- return NULL;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL || socket_arg != SOCKET_ID_ANY)
- return ret;
-
- /* try other heaps */
- for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
- /* we already tried this one */
- if (i == socket)
- continue;
-
- ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
- if (ret != NULL)
- return ret;
- }
-
- return NULL;
-}
-
-/*
- * Allocate memory on default heap.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align)
-{
- return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
-{
- void *ptr = rte_malloc_socket(type, size, align, socket);
-
- if (ptr != NULL)
- memset(ptr, 0, size);
- return ptr;
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align)
-{
- return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
-}
-
-/*
- * Allocate zero'd memory on specified heap.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
-{
- return rte_zmalloc_socket(type, num * size, align, socket);
-}
-
-/*
- * Allocate zero'd memory on default heap.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align)
-{
- return rte_zmalloc(type, num * size, align);
-}
-
-/*
- * Resize allocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align)
-{
- if (ptr == NULL)
- return rte_malloc(NULL, size, align);
-
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (elem == NULL)
- rte_panic("Fatal error: memory corruption detected\n");
-
- size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
- /* check alignment matches first, and if ok, see if we can resize block */
- if (RTE_PTR_ALIGN(ptr,align) == ptr &&
- malloc_elem_resize(elem, size) == 0)
- return ptr;
-
- /* either alignment is off, or we have no room to expand,
- * so move data. */
- void *new_ptr = rte_malloc(NULL, size, align);
- if (new_ptr == NULL)
- return NULL;
- const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
- rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
- rte_free(ptr);
-
- return new_ptr;
-}
-
-int
-rte_malloc_validate(const void *ptr, size_t *size)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(ptr);
- if (!malloc_elem_cookies_ok(elem))
- return -1;
- if (size != NULL)
- *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
- return 0;
-}
-
-/*
- * Function to retrieve data for heap on given socket
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats)
-{
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
-
- if (socket >= RTE_MAX_NUMA_NODES || socket < 0)
- return -1;
-
- return malloc_heap_get_stats(&mcfg->malloc_heaps[socket], socket_stats);
-}
-
-/*
- * Print stats on memory type. If type is NULL, info on all types is printed
- */
-void
-rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
-{
- unsigned int socket;
- struct rte_malloc_socket_stats sock_stats;
- /* Iterate through all initialised heaps */
- for (socket=0; socket< RTE_MAX_NUMA_NODES; socket++) {
- if ((rte_malloc_get_socket_stats(socket, &sock_stats) < 0))
- continue;
-
- fprintf(f, "Socket:%u\n", socket);
- fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
- fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
- fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
- fprintf(f, "\tGreatest_free_size:%zu,\n",
- sock_stats.greatest_free_size);
- fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
- fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
- }
- return;
-}
-
-/*
- * TODO: Set limit to memory that can be allocated to memory type
- */
-int
-rte_malloc_set_limit(__rte_unused const char *type,
- __rte_unused size_t max)
-{
- return 0;
-}
-
-/*
- * Return the physical address of a virtual address obtained through rte_malloc
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr)
-{
- const struct malloc_elem *elem = malloc_elem_from_data(addr);
- if (elem == NULL)
- return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
-}
+/* Empty file to be able to create a dummy library for deprecation policy */
diff --git a/lib/librte_malloc/rte_malloc.h b/lib/librte_malloc/rte_malloc.h
deleted file mode 100644
index 74bb78c..0000000
--- a/lib/librte_malloc/rte_malloc.h
+++ /dev/null
@@ -1,342 +0,0 @@
-/*-
- * BSD LICENSE
- *
- * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef _RTE_MALLOC_H_
-#define _RTE_MALLOC_H_
-
-/**
- * @file
- * RTE Malloc. This library provides methods for dynamically allocating memory
- * from hugepages.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <rte_memory.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Structure to hold heap statistics obtained from rte_malloc_get_socket_stats function.
- */
-struct rte_malloc_socket_stats {
- size_t heap_totalsz_bytes; /**< Total bytes on heap */
- size_t heap_freesz_bytes; /**< Total free bytes on heap */
- size_t greatest_free_size; /**< Size in bytes of largest free block */
- unsigned free_count; /**< Number of free elements on heap */
- unsigned alloc_count; /**< Number of allocated elements on heap */
- size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
-};
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared. In NUMA systems, the memory allocated resides on the same
- * NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc(const char *type, size_t size, unsigned align);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc(const char *type, size_t size, unsigned align);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros. In NUMA systems, the memory allocated resides on the
- * same NUMA socket as the core that calls this function.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc(const char *type, size_t num, size_t size, unsigned align);
-
-/**
- * Replacement function for realloc(), using huge-page memory. Reserved area
- * memory is resized, preserving contents. In NUMA systems, the new area
- * resides on the same NUMA socket as the old area.
- *
- * @param ptr
- * Pointer to already allocated memory
- * @param size
- * Size (in bytes) of new area. If this is 0, memory is freed.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the reallocated memory.
- */
-void *
-rte_realloc(void *ptr, size_t size, unsigned align);
-
-/**
- * This function allocates memory from the huge-page area of memory. The memory
- * is not cleared.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_malloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_malloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Allocate zero'ed memory from the heap.
- *
- * Equivalent to rte_malloc() except that the memory zone is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param size
- * Size (in bytes) to be allocated.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_zmalloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket);
-
-/**
- * Replacement function for calloc(), using huge-page memory. Memory area is
- * initialised with zeros.
- *
- * @param type
- * A string identifying the type of allocated objects (useful for debug
- * purposes, such as identifying the cause of a memory leak). Can be NULL.
- * @param num
- * Number of elements to be allocated.
- * @param size
- * Size (in bytes) of a single element.
- * @param align
- * If 0, the return is a pointer that is suitably aligned for any kind of
- * variable (in the same manner as malloc()).
- * Otherwise, the return is a pointer that is a multiple of *align*. In
- * this case, it must obviously be a power of two. (Minimum alignment is the
- * cacheline size, i.e. 64-bytes)
- * @param socket
- * NUMA socket to allocate memory on. If SOCKET_ID_ANY is used, this function
- * will behave the same as rte_calloc().
- * @return
- * - NULL on error. Not enough memory, or invalid arguments (size is 0,
- * align is not a power of two).
- * - Otherwise, the pointer to the allocated object.
- */
-void *
-rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket);
-
-/**
- * Frees the memory space pointed to by the provided pointer.
- *
- * This pointer must have been returned by a previous call to
- * rte_malloc(), rte_zmalloc(), rte_calloc() or rte_realloc(). The behaviour of
- * rte_free() is undefined if the pointer does not match this requirement.
- *
- * If the pointer is NULL, the function does nothing.
- *
- * @param ptr
- * The pointer to memory to be freed.
- */
-void
-rte_free(void *ptr);
-
-/**
- * If malloc debug is enabled, check a memory block for header
- * and trailer markers to indicate that all is well with the block.
- * If size is non-null, also return the size of the block.
- *
- * @param ptr
- * pointer to the start of a data block, must have been returned
- * by a previous call to rte_malloc(), rte_zmalloc(), rte_calloc()
- * or rte_realloc()
- * @param size
- * if non-null, and memory block pointer is valid, returns the size
- * of the memory block
- * @return
- * -1 on error, invalid pointer passed or header and trailer markers
- * are missing or corrupted
- * 0 on success
- */
-int
-rte_malloc_validate(const void *ptr, size_t *size);
-
-/**
- * Get heap statistics for the specified heap.
- *
- * @param socket
- * An unsigned integer specifying the socket to get heap statistics for
- * @param socket_stats
- * A structure which provides memory to store statistics
- * @return
- * Null on error
- * Pointer to structure storing statistics on success
- */
-int
-rte_malloc_get_socket_stats(int socket,
- struct rte_malloc_socket_stats *socket_stats);
-
-/**
- * Dump statistics.
- *
- * Dump for the specified type to the console. If the type argument is
- * NULL, all memory types will be dumped.
- *
- * @param f
- * A pointer to a file for output
- * @param type
- * A string identifying the type of objects to dump, or NULL
- * to dump all objects.
- */
-void
-rte_malloc_dump_stats(FILE *f, const char *type);
-
-/**
- * Set the maximum amount of allocated memory for this type.
- *
- * This is not yet implemented
- *
- * @param type
- * A string identifying the type of allocated objects.
- * @param max
- * The maximum amount of allocated bytes for this type.
- * @return
- * - 0: Success.
- * - (-1): Error.
- */
-int
-rte_malloc_set_limit(const char *type, size_t max);
-
-/**
- * Return the physical address of a virtual address obtained through
- * rte_malloc
- *
- * @param addr
- * Adress obtained from a previous rte_malloc call
- * @return
- * NULL on error
- * otherwise return physical address of the buffer
- */
-phys_addr_t
-rte_malloc_virt2phy(const void *addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _RTE_MALLOC_H_ */
diff --git a/lib/librte_malloc/rte_malloc_version.map b/lib/librte_malloc/rte_malloc_version.map
index af6ae9b..63cb5fc 100644
--- a/lib/librte_malloc/rte_malloc_version.map
+++ b/lib/librte_malloc/rte_malloc_version.map
@@ -1,19 +1,3 @@
DPDK_2.0 {
- global:
-
- rte_calloc;
- rte_calloc_socket;
- rte_free;
- rte_malloc;
- rte_malloc_dump_stats;
- rte_malloc_get_socket_stats;
- rte_malloc_set_limit;
- rte_malloc_socket;
- rte_malloc_validate;
- rte_malloc_virt2phy;
- rte_realloc;
- rte_zmalloc;
- rte_zmalloc_socket;
-
local: *;
};
diff --git a/lib/librte_mempool/Makefile b/lib/librte_mempool/Makefile
index 940d1f7..a6898ef 100644
--- a/lib/librte_mempool/Makefile
+++ b/lib/librte_mempool/Makefile
@@ -48,8 +48,6 @@ endif
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_MEMPOOL)-include := rte_mempool.h
-# this lib needs eal, rte_ring and rte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_eal lib/librte_ring
-DEPDIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += lib/librte_malloc
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_port/Makefile b/lib/librte_port/Makefile
index de960fc..ddbb383 100644
--- a/lib/librte_port/Makefile
+++ b/lib/librte_port/Makefile
@@ -70,7 +70,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_PORT)-include += rte_port_source_sink.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ether
DEPDIRS-$(CONFIG_RTE_LIBRTE_PORT) += lib/librte_ip_frag
diff --git a/lib/librte_ring/Makefile b/lib/librte_ring/Makefile
index 84ad3d3..4b1112e 100644
--- a/lib/librte_ring/Makefile
+++ b/lib/librte_ring/Makefile
@@ -46,7 +46,6 @@ SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_ring.c
# install includes
SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_ring.h
-# this lib needs eal and rte_malloc
-DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal lib/librte_malloc
+DEPDIRS-$(CONFIG_RTE_LIBRTE_RING) += lib/librte_eal
include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_table/Makefile b/lib/librte_table/Makefile
index 0d8394c..c5b3eaf 100644
--- a/lib/librte_table/Makefile
+++ b/lib/librte_table/Makefile
@@ -75,7 +75,6 @@ SYMLINK-$(CONFIG_RTE_LIBRTE_TABLE)-include += rte_table_stub.h
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) := lib/librte_eal
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mbuf
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_mempool
-DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_malloc
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_port
DEPDIRS-$(CONFIG_RTE_LIBRTE_TABLE) += lib/librte_lpm
ifeq ($(CONFIG_RTE_LIBRTE_ACL),y)
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 2/9] eal: memzone allocated by malloc
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
` (7 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
In the current memory hierarchy, memsegs are groups of physically
contiguous hugepages, memzones are slices of memsegs and malloc further
slices memzones into smaller memory chunks.
This patch modifies malloc so it partitions memsegs instead of memzones.
Thus memzones would call malloc internally for memory allocation while
maintaining its ABI.
It would be possible to free memzones and therefore any other structure
based on memzones, ie. mempools
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/eal_common_memzone.c | 290 ++++++----------------
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_malloc_heap.h | 3 +-
lib/librte_eal/common/malloc_elem.c | 68 +++--
lib/librte_eal/common/malloc_elem.h | 14 +-
lib/librte_eal/common/malloc_heap.c | 161 ++++++------
lib/librte_eal/common/malloc_heap.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 10 +-
lib/librte_eal/linuxapp/eal/eal_memory.c | 2 +-
9 files changed, 226 insertions(+), 330 deletions(-)
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 9c1da71..31bf6d8 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -50,15 +50,15 @@
#include <rte_string_fns.h>
#include <rte_common.h>
+#include "malloc_heap.h"
+#include "malloc_elem.h"
#include "eal_private.h"
-/* internal copy of free memory segments */
-static struct rte_memseg *free_memseg = NULL;
-
static inline const struct rte_memzone *
memzone_lookup_thread_unsafe(const char *name)
{
const struct rte_mem_config *mcfg;
+ const struct rte_memzone *mz;
unsigned i = 0;
/* get pointer to global configuration */
@@ -68,62 +68,50 @@ memzone_lookup_thread_unsafe(const char *name)
* the algorithm is not optimal (linear), but there are few
* zones and this function should be called at init only
*/
- for (i = 0; i < RTE_MAX_MEMZONE && mcfg->memzone[i].addr != NULL; i++) {
- if (!strncmp(name, mcfg->memzone[i].name, RTE_MEMZONE_NAMESIZE))
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ mz = &mcfg->memzone[i];
+ if (mz->addr != NULL && !strncmp(name, mz->name, RTE_MEMZONE_NAMESIZE))
return &mcfg->memzone[i];
}
return NULL;
}
-/*
- * Helper function for memzone_reserve_aligned_thread_unsafe().
- * Calculate address offset from the start of the segment.
- * Align offset in that way that it satisfy istart alignmnet and
- * buffer of the requested length would not cross specified boundary.
- */
-static inline phys_addr_t
-align_phys_boundary(const struct rte_memseg *ms, size_t len, size_t align,
- size_t bound)
+/* This function will return the greatest free block if a heap has been
+ * specified. If no heap has been specified, it will return the heap and
+ * length of the greatest free block available in all heaps */
+static size_t
+find_heap_max_free_elem(int *s, unsigned align)
{
- phys_addr_t addr_offset, bmask, end, start;
- size_t step;
-
- step = RTE_MAX(align, bound);
- bmask = ~((phys_addr_t)bound - 1);
-
- /* calculate offset to closest alignment */
- start = RTE_ALIGN_CEIL(ms->phys_addr, align);
- addr_offset = start - ms->phys_addr;
+ struct rte_mem_config *mcfg;
+ struct rte_malloc_socket_stats stats;
+ int i, socket = *s;
+ size_t len = 0;
- while (addr_offset + len < ms->len) {
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
- /* check, do we meet boundary condition */
- end = start + len - (len != 0);
- if ((start & bmask) == (end & bmask))
- break;
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ if ((socket != SOCKET_ID_ANY) && (socket != i))
+ continue;
- /* calculate next offset */
- start = RTE_ALIGN_CEIL(start + 1, step);
- addr_offset = start - ms->phys_addr;
+ malloc_heap_get_stats(&mcfg->malloc_heaps[i], &stats);
+ if (stats.greatest_free_size > len) {
+ len = stats.greatest_free_size;
+ *s = i;
+ }
}
- return addr_offset;
+ return (len - MALLOC_ELEM_OVERHEAD - align);
}
static const struct rte_memzone *
memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
- int socket_id, uint64_t size_mask, unsigned align,
- unsigned bound)
+ int socket_id, unsigned flags, unsigned align, unsigned bound)
{
struct rte_mem_config *mcfg;
- unsigned i = 0;
- int memseg_idx = -1;
- uint64_t addr_offset, seg_offset = 0;
size_t requested_len;
- size_t memseg_len = 0;
- phys_addr_t memseg_physaddr;
- void *memseg_addr;
+ int socket, i;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -155,7 +143,6 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
if (align < RTE_CACHE_LINE_SIZE)
align = RTE_CACHE_LINE_SIZE;
-
/* align length on cache boundary. Check for overflow before doing so */
if (len > SIZE_MAX - RTE_CACHE_LINE_MASK) {
rte_errno = EINVAL; /* requested size too big */
@@ -169,108 +156,65 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
requested_len = RTE_MAX((size_t)RTE_CACHE_LINE_SIZE, len);
/* check that boundary condition is valid */
- if (bound != 0 &&
- (requested_len > bound || !rte_is_power_of_2(bound))) {
+ if (bound != 0 && (requested_len > bound || !rte_is_power_of_2(bound))) {
rte_errno = EINVAL;
return NULL;
}
- /* find the smallest segment matching requirements */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- /* last segment */
- if (free_memseg[i].addr == NULL)
- break;
-
- /* empty segment, skip it */
- if (free_memseg[i].len == 0)
- continue;
-
- /* bad socket ID */
- if (socket_id != SOCKET_ID_ANY &&
- free_memseg[i].socket_id != SOCKET_ID_ANY &&
- socket_id != free_memseg[i].socket_id)
- continue;
-
- /*
- * calculate offset to closest alignment that
- * meets boundary conditions.
- */
- addr_offset = align_phys_boundary(free_memseg + i,
- requested_len, align, bound);
+ if ((socket_id != SOCKET_ID_ANY) && (socket_id >= RTE_MAX_NUMA_NODES)) {
+ rte_errno = EINVAL;
+ return NULL;
+ }
- /* check len */
- if ((requested_len + addr_offset) > free_memseg[i].len)
- continue;
+ if (!rte_eal_has_hugepages())
+ socket_id = SOCKET_ID_ANY;
- if ((size_mask & free_memseg[i].hugepage_sz) == 0)
- continue;
+ if (len == 0) {
+ if (bound != 0)
+ requested_len = bound;
+ else
+ requested_len = find_heap_max_free_elem(&socket_id, align);
+ }
- /* this segment is the best until now */
- if (memseg_idx == -1) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- /* find the biggest contiguous zone */
- else if (len == 0) {
- if (free_memseg[i].len > memseg_len) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
- }
- }
- /*
- * find the smallest (we already checked that current
- * zone length is > len
- */
- else if (free_memseg[i].len + align < memseg_len ||
- (free_memseg[i].len <= memseg_len + align &&
- addr_offset < seg_offset)) {
- memseg_idx = i;
- memseg_len = free_memseg[i].len;
- seg_offset = addr_offset;
+ if (socket_id == SOCKET_ID_ANY)
+ socket = malloc_get_numa_socket();
+ else
+ socket = socket_id;
+
+ /* allocate memory on heap */
+ void *mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[socket], NULL,
+ requested_len, flags, align, bound);
+
+ if ((mz_addr == NULL) && (socket_id == SOCKET_ID_ANY)) {
+ /* try other heaps */
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ if (socket == i)
+ continue;
+
+ mz_addr = malloc_heap_alloc(&mcfg->malloc_heaps[i],
+ NULL, requested_len, flags, align, bound);
+ if (mz_addr != NULL)
+ break;
}
}
- /* no segment found */
- if (memseg_idx == -1) {
+ if (mz_addr == NULL) {
rte_errno = ENOMEM;
return NULL;
}
- /* save aligned physical and virtual addresses */
- memseg_physaddr = free_memseg[memseg_idx].phys_addr + seg_offset;
- memseg_addr = RTE_PTR_ADD(free_memseg[memseg_idx].addr,
- (uintptr_t) seg_offset);
-
- /* if we are looking for a biggest memzone */
- if (len == 0) {
- if (bound == 0)
- requested_len = memseg_len - seg_offset;
- else
- requested_len = RTE_ALIGN_CEIL(memseg_physaddr + 1,
- bound) - memseg_physaddr;
- }
-
- /* set length to correct value */
- len = (size_t)seg_offset + requested_len;
-
- /* update our internal state */
- free_memseg[memseg_idx].len -= len;
- free_memseg[memseg_idx].phys_addr += len;
- free_memseg[memseg_idx].addr =
- (char *)free_memseg[memseg_idx].addr + len;
+ const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
snprintf(mz->name, sizeof(mz->name), "%s", name);
- mz->phys_addr = memseg_physaddr;
- mz->addr = memseg_addr;
- mz->len = requested_len;
- mz->hugepage_sz = free_memseg[memseg_idx].hugepage_sz;
- mz->socket_id = free_memseg[memseg_idx].socket_id;
+ mz->phys_addr = rte_malloc_virt2phy(mz_addr);
+ mz->addr = mz_addr;
+ mz->len = (requested_len == 0 ? elem->size : requested_len);
+ mz->hugepage_sz = elem->ms->hugepage_sz;
+ mz->socket_id = elem->ms->socket_id;
mz->flags = 0;
- mz->memseg_id = memseg_idx;
+ mz->memseg_id = elem->ms - rte_eal_get_configuration()->mem_config->memseg;
return mz;
}
@@ -282,26 +226,6 @@ rte_memzone_reserve_thread_safe(const char *name, size_t len,
{
struct rte_mem_config *mcfg;
const struct rte_memzone *mz = NULL;
- uint64_t size_mask = 0;
-
- if (flags & RTE_MEMZONE_256KB)
- size_mask |= RTE_PGSIZE_256K;
- if (flags & RTE_MEMZONE_2MB)
- size_mask |= RTE_PGSIZE_2M;
- if (flags & RTE_MEMZONE_16MB)
- size_mask |= RTE_PGSIZE_16M;
- if (flags & RTE_MEMZONE_256MB)
- size_mask |= RTE_PGSIZE_256M;
- if (flags & RTE_MEMZONE_512MB)
- size_mask |= RTE_PGSIZE_512M;
- if (flags & RTE_MEMZONE_1GB)
- size_mask |= RTE_PGSIZE_1G;
- if (flags & RTE_MEMZONE_4GB)
- size_mask |= RTE_PGSIZE_4G;
- if (flags & RTE_MEMZONE_16GB)
- size_mask |= RTE_PGSIZE_16G;
- if (!size_mask)
- size_mask = UINT64_MAX;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
@@ -309,18 +233,7 @@ rte_memzone_reserve_thread_safe(const char *name, size_t len,
rte_rwlock_write_lock(&mcfg->mlock);
mz = memzone_reserve_aligned_thread_unsafe(
- name, len, socket_id, size_mask, align, bound);
-
- /*
- * If we failed to allocate the requested page size, and the
- * RTE_MEMZONE_SIZE_HINT_ONLY flag is specified, try allocating
- * again.
- */
- if (!mz && rte_errno == ENOMEM && size_mask != UINT64_MAX &&
- flags & RTE_MEMZONE_SIZE_HINT_ONLY) {
- mz = memzone_reserve_aligned_thread_unsafe(
- name, len, socket_id, UINT64_MAX, align, bound);
- }
+ name, len, socket_id, flags, align, bound);
rte_rwlock_write_unlock(&mcfg->mlock);
@@ -412,45 +325,6 @@ rte_memzone_dump(FILE *f)
}
/*
- * called by init: modify the free memseg list to have cache-aligned
- * addresses and cache-aligned lengths
- */
-static int
-memseg_sanitize(struct rte_memseg *memseg)
-{
- unsigned phys_align;
- unsigned virt_align;
- unsigned off;
-
- phys_align = memseg->phys_addr & RTE_CACHE_LINE_MASK;
- virt_align = (unsigned long)memseg->addr & RTE_CACHE_LINE_MASK;
-
- /*
- * sanity check: phys_addr and addr must have the same
- * alignment
- */
- if (phys_align != virt_align)
- return -1;
-
- /* memseg is really too small, don't bother with it */
- if (memseg->len < (2 * RTE_CACHE_LINE_SIZE)) {
- memseg->len = 0;
- return 0;
- }
-
- /* align start address */
- off = (RTE_CACHE_LINE_SIZE - phys_align) & RTE_CACHE_LINE_MASK;
- memseg->phys_addr += off;
- memseg->addr = (char *)memseg->addr + off;
- memseg->len -= off;
-
- /* align end address */
- memseg->len &= ~((uint64_t)RTE_CACHE_LINE_MASK);
-
- return 0;
-}
-
-/*
* Init the memzone subsystem
*/
int
@@ -458,14 +332,10 @@ rte_eal_memzone_init(void)
{
struct rte_mem_config *mcfg;
const struct rte_memseg *memseg;
- unsigned i = 0;
/* get pointer to global configuration */
mcfg = rte_eal_get_configuration()->mem_config;
- /* mirror the runtime memsegs from config */
- free_memseg = mcfg->free_memseg;
-
/* secondary processes don't need to initialise anything */
if (rte_eal_process_type() == RTE_PROC_SECONDARY)
return 0;
@@ -478,33 +348,13 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
- /* fill in uninitialized free_memsegs */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (memseg[i].addr == NULL)
- break;
- if (free_memseg[i].addr != NULL)
- continue;
- memcpy(&free_memseg[i], &memseg[i], sizeof(struct rte_memseg));
- }
-
- /* make all zones cache-aligned */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- if (free_memseg[i].addr == NULL)
- break;
- if (memseg_sanitize(&free_memseg[i]) < 0) {
- RTE_LOG(ERR, EAL, "%s(): Sanity check failed\n", __func__);
- rte_rwlock_write_unlock(&mcfg->mlock);
- return -1;
- }
- }
-
/* delete all zones */
mcfg->memzone_idx = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
- return 0;
+ return rte_eal_malloc_heap_init();
}
/* Walk all reserved memory zones */
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 34f5abc..055212a 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,7 +73,7 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors. */
+ /* Runtime Physmem descriptors - NOT USED */
struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
diff --git a/lib/librte_eal/common/include/rte_malloc_heap.h b/lib/librte_eal/common/include/rte_malloc_heap.h
index 716216f..b270356 100644
--- a/lib/librte_eal/common/include/rte_malloc_heap.h
+++ b/lib/librte_eal/common/include/rte_malloc_heap.h
@@ -40,7 +40,7 @@
#include <rte_memory.h>
/* Number of free lists per heap, grouped by size. */
-#define RTE_HEAP_NUM_FREELISTS 5
+#define RTE_HEAP_NUM_FREELISTS 13
/**
* Structure to hold malloc heap
@@ -48,7 +48,6 @@
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
- unsigned mz_count;
unsigned alloc_count;
size_t total_size;
} __rte_cache_aligned;
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index a5e1248..b54ee33 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -37,7 +37,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_launch.h>
#include <rte_per_lcore.h>
@@ -56,10 +55,10 @@
*/
void
malloc_elem_init(struct malloc_elem *elem,
- struct malloc_heap *heap, const struct rte_memzone *mz, size_t size)
+ struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
{
elem->heap = heap;
- elem->mz = mz;
+ elem->ms = ms;
elem->prev = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
@@ -70,12 +69,12 @@ malloc_elem_init(struct malloc_elem *elem,
}
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
{
- malloc_elem_init(elem, prev->heap, prev->mz, 0);
+ malloc_elem_init(elem, prev->heap, prev->ms, 0);
elem->prev = prev;
elem->state = ELEM_BUSY; /* mark busy so its never merged */
}
@@ -86,12 +85,24 @@ malloc_elem_mkend(struct malloc_elem *elem, struct malloc_elem *prev)
* fit, return NULL.
*/
static void *
-elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
+elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- const uintptr_t end_pt = (uintptr_t)elem +
+ const size_t bmask = ~(bound - 1);
+ uintptr_t end_pt = (uintptr_t)elem +
elem->size - MALLOC_ELEM_TRAILER_LEN;
- const uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
- const uintptr_t new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+ uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ uintptr_t new_elem_start;
+
+ /* check boundary */
+ if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+ end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+ new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
+ if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+ return NULL;
+ }
+
+ new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
/* if the new start point is before the exist start, it won't fit */
return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
@@ -102,9 +113,10 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align)
* alignment request from the current element
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- return elem_start_pt(elem, size, align) != NULL;
+ return elem_start_pt(elem, size, align, bound) != NULL;
}
/*
@@ -115,10 +127,10 @@ static void
split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
{
struct malloc_elem *next_elem = RTE_PTR_ADD(elem, elem->size);
- const unsigned old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
- const unsigned new_elem_size = elem->size - old_elem_size;
+ const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
+ const size_t new_elem_size = elem->size - old_elem_size;
- malloc_elem_init(split_pt, elem->heap, elem->mz, new_elem_size);
+ malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
split_pt->prev = elem;
next_elem->prev = split_pt;
elem->size = old_elem_size;
@@ -168,8 +180,9 @@ malloc_elem_free_list_index(size_t size)
void
malloc_elem_free_list_insert(struct malloc_elem *elem)
{
- size_t idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
+ size_t idx;
+ idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
elem->state = ELEM_FREE;
LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
}
@@ -190,12 +203,26 @@ elem_free_list_remove(struct malloc_elem *elem)
* is not done here, as it's done there previously.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
+malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
+ size_t bound)
{
- struct malloc_elem *new_elem = elem_start_pt(elem, size, align);
- const unsigned old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+ const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
+ const size_t trailer_size = elem->size - old_elem_size - size -
+ MALLOC_ELEM_OVERHEAD;
+
+ elem_free_list_remove(elem);
- if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE){
+ if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+ /* split it, too much free space after elem */
+ struct malloc_elem *new_free_elem =
+ RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
+
+ split_elem(elem, new_free_elem);
+ malloc_elem_free_list_insert(new_free_elem);
+ }
+
+ if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
/* don't split it, pad the element instead */
elem->state = ELEM_BUSY;
elem->pad = old_elem_size;
@@ -208,8 +235,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
new_elem->size = elem->size - elem->pad;
set_header(new_elem);
}
- /* remove element from free list */
- elem_free_list_remove(elem);
return new_elem;
}
@@ -219,7 +244,6 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align)
* Re-insert original element, in case its new size makes it
* belong on a different list.
*/
- elem_free_list_remove(elem);
split_elem(elem, new_elem);
new_elem->state = ELEM_BUSY;
malloc_elem_free_list_insert(elem);
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 9790b1a..e05d2ea 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -47,9 +47,9 @@ enum elem_state {
struct malloc_elem {
struct malloc_heap *heap;
- struct malloc_elem *volatile prev; /* points to prev elem in memzone */
+ struct malloc_elem *volatile prev; /* points to prev elem in memseg */
LIST_ENTRY(malloc_elem) free_list; /* list of free elements in heap */
- const struct rte_memzone *mz;
+ const struct rte_memseg *ms;
volatile enum elem_state state;
uint32_t pad;
size_t size;
@@ -136,11 +136,11 @@ malloc_elem_from_data(const void *data)
void
malloc_elem_init(struct malloc_elem *elem,
struct malloc_heap *heap,
- const struct rte_memzone *mz,
+ const struct rte_memseg *ms,
size_t size);
/*
- * initialise a dummy malloc_elem header for the end-of-memzone marker
+ * initialise a dummy malloc_elem header for the end-of-memseg marker
*/
void
malloc_elem_mkend(struct malloc_elem *elem,
@@ -151,14 +151,16 @@ malloc_elem_mkend(struct malloc_elem *elem,
* of the requested size and with the requested alignment
*/
int
-malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_can_hold(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* reserve a block of data in an existing malloc_elem. If the malloc_elem
* is much larger than the data block requested, we split the element in two.
*/
struct malloc_elem *
-malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align);
+malloc_elem_alloc(struct malloc_elem *elem, size_t size,
+ unsigned align, size_t bound);
/*
* free a malloc_elem block by adding it to the free list. If the
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index 8861d27..21d8914 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -39,7 +39,6 @@
#include <sys/queue.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_launch.h>
@@ -54,123 +53,125 @@
#include "malloc_elem.h"
#include "malloc_heap.h"
-/* since the memzone size starts with a digit, it will appear unquoted in
- * rte_config.h, so quote it so it can be passed to rte_str_to_size */
-#define MALLOC_MEMZONE_SIZE RTE_STR(RTE_MALLOC_MEMZONE_SIZE)
-
-/*
- * returns the configuration setting for the memzone size as a size_t value
- */
-static inline size_t
-get_malloc_memzone_size(void)
+static unsigned
+check_hugepage_sz(unsigned flags, uint64_t hugepage_sz)
{
- return rte_str_to_size(MALLOC_MEMZONE_SIZE);
+ unsigned check_flag = 0;
+
+ if (!(flags & ~RTE_MEMZONE_SIZE_HINT_ONLY))
+ return 1;
+
+ switch (hugepage_sz) {
+ case RTE_PGSIZE_256K:
+ check_flag = RTE_MEMZONE_256KB;
+ break;
+ case RTE_PGSIZE_2M:
+ check_flag = RTE_MEMZONE_2MB;
+ break;
+ case RTE_PGSIZE_16M:
+ check_flag = RTE_MEMZONE_16MB;
+ break;
+ case RTE_PGSIZE_256M:
+ check_flag = RTE_MEMZONE_256MB;
+ break;
+ case RTE_PGSIZE_512M:
+ check_flag = RTE_MEMZONE_512MB;
+ break;
+ case RTE_PGSIZE_1G:
+ check_flag = RTE_MEMZONE_1GB;
+ break;
+ case RTE_PGSIZE_4G:
+ check_flag = RTE_MEMZONE_4GB;
+ break;
+ case RTE_PGSIZE_16G:
+ check_flag = RTE_MEMZONE_16GB;
+ }
+
+ return (check_flag & flags);
}
/*
- * reserve an extra memory zone and make it available for use by a particular
- * heap. This reserves the zone and sets a dummy malloc_elem header at the end
+ * Expand the heap with a memseg.
+ * This reserves the zone and sets a dummy malloc_elem header at the end
* to prevent overflow. The rest of the zone is added to free list as a single
* large free block
*/
-static int
-malloc_heap_add_memzone(struct malloc_heap *heap, size_t size, unsigned align)
+static void
+malloc_heap_add_memseg(struct malloc_heap *heap, struct rte_memseg *ms)
{
- const unsigned mz_flags = 0;
- const size_t block_size = get_malloc_memzone_size();
- /* ensure the data we want to allocate will fit in the memzone */
- const size_t min_size = size + align + MALLOC_ELEM_OVERHEAD * 2;
- const struct rte_memzone *mz = NULL;
- struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
- unsigned numa_socket = heap - mcfg->malloc_heaps;
-
- size_t mz_size = min_size;
- if (mz_size < block_size)
- mz_size = block_size;
-
- char mz_name[RTE_MEMZONE_NAMESIZE];
- snprintf(mz_name, sizeof(mz_name), "MALLOC_S%u_HEAP_%u",
- numa_socket, heap->mz_count++);
-
- /* try getting a block. if we fail and we don't need as big a block
- * as given in the config, we can shrink our request and try again
- */
- do {
- mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
- mz_flags);
- if (mz == NULL)
- mz_size /= 2;
- } while (mz == NULL && mz_size > min_size);
- if (mz == NULL)
- return -1;
-
/* allocate the memory block headers, one at end, one at start */
- struct malloc_elem *start_elem = (struct malloc_elem *)mz->addr;
- struct malloc_elem *end_elem = RTE_PTR_ADD(mz->addr,
- mz_size - MALLOC_ELEM_OVERHEAD);
+ struct malloc_elem *start_elem = (struct malloc_elem *)ms->addr;
+ struct malloc_elem *end_elem = RTE_PTR_ADD(ms->addr,
+ ms->len - MALLOC_ELEM_OVERHEAD);
end_elem = RTE_PTR_ALIGN_FLOOR(end_elem, RTE_CACHE_LINE_SIZE);
+ const size_t elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- const unsigned elem_size = (uintptr_t)end_elem - (uintptr_t)start_elem;
- malloc_elem_init(start_elem, heap, mz, elem_size);
+ malloc_elem_init(start_elem, heap, ms, elem_size);
malloc_elem_mkend(end_elem, start_elem);
malloc_elem_free_list_insert(start_elem);
- /* increase heap total size by size of new memzone */
- heap->total_size+=mz_size - MALLOC_ELEM_OVERHEAD;
- return 0;
+ heap->total_size += elem_size;
}
/*
* Iterates through the freelist for a heap to find a free element
* which can store data of the required size and with the requested alignment.
+ * If size is 0, find the biggest available elem.
* Returns null on failure, or pointer to element on success.
*/
static struct malloc_elem *
-find_suitable_element(struct malloc_heap *heap, size_t size, unsigned align)
+find_suitable_element(struct malloc_heap *heap, size_t size,
+ unsigned flags, size_t align, size_t bound)
{
size_t idx;
- struct malloc_elem *elem;
+ struct malloc_elem *elem, *alt_elem = NULL;
for (idx = malloc_elem_free_list_index(size);
- idx < RTE_HEAP_NUM_FREELISTS; idx++)
- {
+ idx < RTE_HEAP_NUM_FREELISTS; idx++) {
for (elem = LIST_FIRST(&heap->free_head[idx]);
- !!elem; elem = LIST_NEXT(elem, free_list))
- {
- if (malloc_elem_can_hold(elem, size, align))
- return elem;
+ !!elem; elem = LIST_NEXT(elem, free_list)) {
+ if (malloc_elem_can_hold(elem, size, align, bound)) {
+ if (check_hugepage_sz(flags, elem->ms->hugepage_sz))
+ return elem;
+ if (alt_elem == NULL)
+ alt_elem = elem;
+ }
}
}
+
+ if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
+ return alt_elem;
+
return NULL;
}
/*
- * Main function called by malloc to allocate a block of memory from the
- * heap. It locks the free list, scans it, and adds a new memzone if the
- * scan fails. Once the new memzone is added, it re-scans and should return
+ * Main function to allocate a block of memory from the heap.
+ * It locks the free list, scans it, and adds a new memseg if the
+ * scan fails. Once the new memseg is added, it re-scans and should return
* the new element after releasing the lock.
*/
void *
malloc_heap_alloc(struct malloc_heap *heap,
- const char *type __attribute__((unused)), size_t size, unsigned align)
+ const char *type __attribute__((unused)), size_t size, unsigned flags,
+ size_t align, size_t bound)
{
+ struct malloc_elem *elem;
+
size = RTE_CACHE_LINE_ROUNDUP(size);
align = RTE_CACHE_LINE_ROUNDUP(align);
+
rte_spinlock_lock(&heap->lock);
- struct malloc_elem *elem = find_suitable_element(heap, size, align);
- if (elem == NULL){
- if ((malloc_heap_add_memzone(heap, size, align)) == 0)
- elem = find_suitable_element(heap, size, align);
- }
- if (elem != NULL){
- elem = malloc_elem_alloc(elem, size, align);
+ elem = find_suitable_element(heap, size, flags, align, bound);
+ if (elem != NULL) {
+ elem = malloc_elem_alloc(elem, size, align, bound);
/* increase heap's count of allocated elements */
heap->alloc_count++;
}
rte_spinlock_unlock(&heap->lock);
- return elem == NULL ? NULL : (void *)(&elem[1]);
+ return elem == NULL ? NULL : (void *)(&elem[1]);
}
/*
@@ -206,3 +207,21 @@ malloc_heap_get_stats(const struct malloc_heap *heap,
socket_stats->alloc_count = heap->alloc_count;
return 0;
}
+
+int
+rte_eal_malloc_heap_init(void)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ unsigned ms_cnt;
+ struct rte_memseg *ms;
+
+ if (mcfg == NULL)
+ return -1;
+
+ for (ms = &mcfg->memseg[0], ms_cnt = 0;
+ (ms_cnt < RTE_MAX_MEMSEG) && (ms->len > 0);
+ ms_cnt++, ms++)
+ malloc_heap_add_memseg(&mcfg->malloc_heaps[ms->socket_id], ms);
+
+ return 0;
+}
diff --git a/lib/librte_eal/common/malloc_heap.h b/lib/librte_eal/common/malloc_heap.h
index a47136d..3ccbef0 100644
--- a/lib/librte_eal/common/malloc_heap.h
+++ b/lib/librte_eal/common/malloc_heap.h
@@ -53,15 +53,15 @@ malloc_get_numa_socket(void)
}
void *
-malloc_heap_alloc(struct malloc_heap *heap, const char *type,
- size_t size, unsigned align);
+malloc_heap_alloc(struct malloc_heap *heap, const char *type, size_t size,
+ unsigned flags, size_t align, size_t bound);
int
malloc_heap_get_stats(const struct malloc_heap *heap,
struct rte_malloc_socket_stats *socket_stats);
int
-rte_eal_heap_memzone_init(void);
+rte_eal_malloc_heap_init(void);
#ifdef __cplusplus
}
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index c313a57..47deb00 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -39,7 +39,6 @@
#include <rte_memcpy.h>
#include <rte_memory.h>
-#include <rte_memzone.h>
#include <rte_eal.h>
#include <rte_eal_memconfig.h>
#include <rte_branch_prediction.h>
@@ -77,6 +76,9 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
if (size == 0 || (align && !rte_is_power_of_2(align)))
return NULL;
+ if (!rte_eal_has_hugepages())
+ socket_arg = SOCKET_ID_ANY;
+
if (socket_arg == SOCKET_ID_ANY)
socket = malloc_get_numa_socket();
else
@@ -87,7 +89,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
return NULL;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[socket], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL || socket_arg != SOCKET_ID_ANY)
return ret;
@@ -98,7 +100,7 @@ rte_malloc_socket(const char *type, size_t size, unsigned align, int socket_arg)
continue;
ret = malloc_heap_alloc(&mcfg->malloc_heaps[i], type,
- size, align == 0 ? 1 : align);
+ size, 0, align == 0 ? 1 : align, 0);
if (ret != NULL)
return ret;
}
@@ -256,5 +258,5 @@ rte_malloc_virt2phy(const void *addr)
const struct malloc_elem *elem = malloc_elem_from_data(addr);
if (elem == NULL)
return 0;
- return elem->mz->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->mz->addr);
+ return elem->ms->phys_addr + ((uintptr_t)addr - (uintptr_t)elem->ms->addr);
}
diff --git a/lib/librte_eal/linuxapp/eal/eal_memory.c b/lib/librte_eal/linuxapp/eal/eal_memory.c
index 4fd63bb..80ee78f 100644
--- a/lib/librte_eal/linuxapp/eal/eal_memory.c
+++ b/lib/librte_eal/linuxapp/eal/eal_memory.c
@@ -1071,7 +1071,7 @@ rte_eal_hugepage_init(void)
mcfg->memseg[0].addr = addr;
mcfg->memseg[0].hugepage_sz = RTE_PGSIZE_4K;
mcfg->memseg[0].len = internal_config.memory;
- mcfg->memseg[0].socket_id = SOCKET_ID_ANY;
+ mcfg->memseg[0].socket_id = 0;
return 0;
}
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 3/9] app/test: update malloc/memzone unit tests
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
` (6 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
Some unit test are not relevant anymore. It is the case of those malloc
UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE
chunks, and the case of those memzone UTs relaying of specific free
memsegs of rhte reserved memzone.
Other UTs just need to be update, for example, to calculate maximum free
block size available.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_malloc.c | 86 ----------
app/test/test_memzone.c | 440 ++++--------------------------------------------
2 files changed, 35 insertions(+), 491 deletions(-)
diff --git a/app/test/test_malloc.c b/app/test/test_malloc.c
index ea6f651..a04a751 100644
--- a/app/test/test_malloc.c
+++ b/app/test/test_malloc.c
@@ -56,10 +56,6 @@
#define N 10000
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
-#define MALLOC_MEMZONE_SIZE QUOTE(RTE_MALLOC_MEMZONE_SIZE)
-
/*
* Malloc
* ======
@@ -292,60 +288,6 @@ test_str_to_size(void)
}
static int
-test_big_alloc(void)
-{
- int socket = 0;
- struct rte_malloc_socket_stats pre_stats, post_stats;
- size_t size =rte_str_to_size(MALLOC_MEMZONE_SIZE)*2;
- int align = 0;
-#ifndef RTE_LIBRTE_MALLOC_DEBUG
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#else
- int overhead = RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE + RTE_CACHE_LINE_SIZE;
-#endif
-
- rte_malloc_get_socket_stats(socket, &pre_stats);
-
- void *p1 = rte_malloc_socket("BIG", size , align, socket);
- if (!p1)
- return -1;
- rte_malloc_get_socket_stats(socket,&post_stats);
-
- /* Check statistics reported are correct */
- /* Allocation may increase, or may be the same as before big allocation */
- if (post_stats.heap_totalsz_bytes < pre_stats.heap_totalsz_bytes) {
- printf("Malloc statistics are incorrect - heap_totalsz_bytes\n");
- return -1;
- }
- /* Check that allocated size adds up correctly */
- if (post_stats.heap_allocsz_bytes !=
- pre_stats.heap_allocsz_bytes + size + align + overhead) {
- printf("Malloc statistics are incorrect - alloc_size\n");
- return -1;
- }
- /* Check free size against tested allocated size */
- if (post_stats.heap_freesz_bytes !=
- post_stats.heap_totalsz_bytes - post_stats.heap_allocsz_bytes) {
- printf("Malloc statistics are incorrect - heap_freesz_bytes\n");
- return -1;
- }
- /* Number of allocated blocks must increase after allocation */
- if (post_stats.alloc_count != pre_stats.alloc_count + 1) {
- printf("Malloc statistics are incorrect - alloc_count\n");
- return -1;
- }
- /* New blocks now available - just allocated 1 but also 1 new free */
- if (post_stats.free_count != pre_stats.free_count &&
- post_stats.free_count != pre_stats.free_count - 1) {
- printf("Malloc statistics are incorrect - free_count\n");
- return -1;
- }
-
- rte_free(p1);
- return 0;
-}
-
-static int
test_multi_alloc_statistics(void)
{
int socket = 0;
@@ -399,10 +341,6 @@ test_multi_alloc_statistics(void)
/* After freeing both allocations check stats return to original */
rte_malloc_get_socket_stats(socket, &post_stats);
- /*
- * Check that no new blocks added after small allocations
- * i.e. < RTE_MALLOC_MEMZONE_SIZE
- */
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
printf("Incorrect heap statistics: Total size \n");
return -1;
@@ -447,18 +385,6 @@ test_multi_alloc_statistics(void)
}
static int
-test_memzone_size_alloc(void)
-{
- void *p1 = rte_malloc("BIG", (size_t)(rte_str_to_size(MALLOC_MEMZONE_SIZE) - 128), 64);
- if (!p1)
- return -1;
- rte_free(p1);
- /* one extra check - check no crashes if free(NULL) */
- rte_free(NULL);
- return 0;
-}
-
-static int
test_rte_malloc_type_limits(void)
{
/* The type-limits functionality is not yet implemented,
@@ -935,18 +861,6 @@ test_malloc(void)
}
else printf("test_str_to_size() passed\n");
- if (test_memzone_size_alloc() < 0){
- printf("test_memzone_size_alloc() failed\n");
- return -1;
- }
- else printf("test_memzone_size_alloc() passed\n");
-
- if (test_big_alloc() < 0){
- printf("test_big_alloc() failed\n");
- return -1;
- }
- else printf("test_big_alloc() passed\n");
-
if (test_zero_aligned_alloc() < 0){
printf("test_zero_aligned_alloc() failed\n");
return -1;
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 9c7a1cb..6934eee 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -44,6 +44,9 @@
#include <rte_eal_memconfig.h>
#include <rte_common.h>
#include <rte_string_fns.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include "../../lib/librte_eal/common/malloc_elem.h"
#include "test.h"
@@ -378,65 +381,37 @@ test_memzone_reserve_flags(void)
return 0;
}
-static int
-test_memzone_reserve_max(void)
+
+/* Find the heap with the greatest free block size */
+static size_t
+find_max_block_free_size(const unsigned _align)
{
- const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
+ struct rte_malloc_socket_stats stats;
+ unsigned i, align = _align;
size_t len = 0;
- void* last_addr;
- size_t maxlen = 0;
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
+ for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+ rte_malloc_get_socket_stats(i, &stats);
+ if (stats.greatest_free_size > len)
+ len = stats.greatest_free_size;
+ }
- ms = rte_eal_get_physmem_layout();
+ if (align < RTE_CACHE_LINE_SIZE)
+ align = RTE_CACHE_LINE_ROUNDUP(align+1);
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference. Addresses
- * are allocated sequentially so we don't need to worry about
- * them being in the right order.
- */
- len -= RTE_PTR_DIFF(
- config->mem_config->memzone[memzone_idx].addr,
- last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr = RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
+ if (len <= MALLOC_ELEM_OVERHEAD + align)
+ return 0;
- /* we don't need to calculate offset here since length
- * is always cache-aligned */
- if (len > maxlen)
- maxlen = len;
- }
+ return len - MALLOC_ELEM_OVERHEAD - align;
+}
+
+static int
+test_memzone_reserve_max(void)
+{
+ const struct rte_memzone *mz;
+ size_t maxlen;
+
+ maxlen = find_max_block_free_size(0);
if (maxlen == 0) {
printf("There is no space left!\n");
@@ -445,7 +420,8 @@ test_memzone_reserve_max(void)
mz = rte_memzone_reserve("max_zone", 0, SOCKET_ID_ANY, 0);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -453,8 +429,7 @@ test_memzone_reserve_max(void)
if (mz->len != maxlen) {
printf("Memzone reserve with 0 size did not return bigest block\n");
- printf("Expected size = %zu, actual size = %zu\n",
- maxlen, mz->len);
+ printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
@@ -467,81 +442,24 @@ static int
test_memzone_reserve_max_aligned(void)
{
const struct rte_memzone *mz;
- const struct rte_config *config;
- const struct rte_memseg *ms;
- int memseg_idx = 0;
- int memzone_idx = 0;
- uintptr_t addr_offset;
- size_t len = 0;
- void* last_addr;
size_t maxlen = 0;
/* random alignment */
rte_srand((unsigned)rte_rdtsc());
const unsigned align = 1 << ((rte_rand() % 8) + 5); /* from 128 up to 4k alignment */
- /* get pointer to global configuration */
- config = rte_eal_get_configuration();
-
- ms = rte_eal_get_physmem_layout();
-
- addr_offset = 0;
-
- for (memseg_idx = 0; memseg_idx < RTE_MAX_MEMSEG; memseg_idx++){
-
- /* ignore smaller memsegs as they can only get smaller */
- if (ms[memseg_idx].len < maxlen)
- continue;
-
- /* align everything */
- last_addr = RTE_PTR_ALIGN_CEIL(ms[memseg_idx].addr, RTE_CACHE_LINE_SIZE);
- len = ms[memseg_idx].len - RTE_PTR_DIFF(last_addr, ms[memseg_idx].addr);
- len &= ~((size_t) RTE_CACHE_LINE_MASK);
-
- /* cycle through all memzones */
- for (memzone_idx = 0; memzone_idx < RTE_MAX_MEMZONE; memzone_idx++) {
-
- /* stop when reaching last allocated memzone */
- if (config->mem_config->memzone[memzone_idx].addr == NULL)
- break;
-
- /* check if the memzone is in our memseg and subtract length */
- if ((config->mem_config->memzone[memzone_idx].addr >=
- ms[memseg_idx].addr) &&
- (config->mem_config->memzone[memzone_idx].addr <
- (RTE_PTR_ADD(ms[memseg_idx].addr, ms[memseg_idx].len)))) {
- /* since the zones can now be aligned and occasionally skip
- * some space, we should calculate the length based on
- * reported length and start addresses difference.
- */
- len -= (uintptr_t) RTE_PTR_SUB(
- config->mem_config->memzone[memzone_idx].addr,
- (uintptr_t) last_addr);
- len -= config->mem_config->memzone[memzone_idx].len;
- last_addr =
- RTE_PTR_ADD(config->mem_config->memzone[memzone_idx].addr,
- (size_t) config->mem_config->memzone[memzone_idx].len);
- }
- }
-
- /* make sure we get the alignment offset */
- if (len > maxlen) {
- addr_offset = RTE_PTR_ALIGN_CEIL((uintptr_t) last_addr, align) - (uintptr_t) last_addr;
- maxlen = len;
- }
- }
+ maxlen = find_max_block_free_size(align);
- if (maxlen == 0 || maxlen == addr_offset) {
+ if (maxlen == 0) {
printf("There is no space left for biggest %u-aligned memzone!\n", align);
return 0;
}
- maxlen -= addr_offset;
-
mz = rte_memzone_reserve_aligned("max_zone_aligned", 0,
SOCKET_ID_ANY, 0, align);
if (mz == NULL){
- printf("Failed to reserve a big chunk of memory\n");
+ printf("Failed to reserve a big chunk of memory - %s\n",
+ rte_strerror(rte_errno));
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
return -1;
@@ -762,282 +680,6 @@ test_memzone_bounded(void)
if ((rc = check_memzone_bounded("bounded_1K_MAX", 0, 64, 1024)) != 0)
return (rc);
- return (0);
-}
-
-static int
-test_memzone_reserve_memory_in_smallest_segment(void)
-{
- const struct rte_memzone *mz;
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t min_len, prev_min_len;
- const struct rte_config *config;
- int i;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len))
- prev_min_ms = ms;
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- min_len = min_ms->len;
- prev_min_len = prev_min_ms->len;
-
- /* try reserving a memzone in the smallest memseg */
- mz = rte_memzone_reserve("smallest_mz", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0);
- if (mz == NULL) {
- printf("Failed to reserve memory from smallest memseg!\n");
- return -1;
- }
- if (prev_min_ms->len != prev_min_len &&
- min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong memseg!\n");
- return -1;
- }
-
- return 0;
-}
-
-/* this test is a bit tricky, and thus warrants explanation.
- *
- * first, we find two smallest memsegs to conduct our experiments on.
- *
- * then, we bring them within alignment from each other: if second segment is
- * twice+ as big as the first, reserve memory from that segment; if second
- * segment is comparable in length to the first, then cut the first segment
- * down until it becomes less than half of second segment, and then cut down
- * the second segment to be within alignment of the first.
- *
- * then, we have to pass the following test: if segments are within alignment
- * of each other (that is, the difference is less than 256 bytes, which is what
- * our alignment will be), segment with smallest offset should be picked.
- *
- * we know that min_ms will be our smallest segment, so we need to make sure
- * that we adjust the alignments so that the bigger segment has smallest
- * alignment (in our case, smallest segment will have 64-byte alignment, while
- * bigger segment will have 128-byte alignment).
- */
-static int
-test_memzone_reserve_memory_with_smallest_offset(void)
-{
- const struct rte_memseg *ms, *min_ms, *prev_min_ms;
- size_t len, min_len, prev_min_len;
- const struct rte_config *config;
- int i, align;
-
- config = rte_eal_get_configuration();
-
- min_ms = NULL; /*< smallest segment */
- prev_min_ms = NULL; /*< second smallest segment */
- align = RTE_CACHE_LINE_SIZE * 4;
-
- /* find two smallest segments */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_ms == NULL)
- min_ms = ms;
- else if (min_ms->len > ms->len) {
- /* set last smallest to second last */
- prev_min_ms = min_ms;
-
- /* set new smallest */
- min_ms = ms;
- } else if ((prev_min_ms == NULL)
- || (prev_min_ms->len > ms->len)) {
- prev_min_ms = ms;
- }
- }
-
- if (min_ms == NULL || prev_min_ms == NULL) {
- printf("Smallest segments not found!\n");
- return -1;
- }
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- /* if smallest segment is bigger than half of bigger segment */
- if (prev_min_ms->len - min_ms->len <= min_ms->len) {
-
- len = (min_ms->len * 2) - prev_min_ms->len;
-
- /* make sure final length is *not* aligned */
- while (((min_ms->addr_64 + len) & (align-1)) == 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz1", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- /* if we don't need to touch smallest segment but it's aligned */
- else if ((min_ms->addr_64 & (align-1)) == 0) {
- if (rte_memzone_reserve("align_mz1", RTE_CACHE_LINE_SIZE,
- SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
- if (min_ms->len != min_len - RTE_CACHE_LINE_SIZE) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
-
- /* if smallest segment is less than half of bigger segment */
- if (prev_min_ms->len - min_ms->len > min_ms->len) {
- len = prev_min_ms->len - min_ms->len - align;
-
- /* make sure final length is aligned */
- while (((prev_min_ms->addr_64 + len) & (align-1)) != 0)
- len += RTE_CACHE_LINE_SIZE;
-
- if (rte_memzone_reserve("dummy_mz2", len, SOCKET_ID_ANY, 0) == NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (prev_min_ms->len != prev_min_len - len) {
- printf("Reserved memory from wrong segment!\n");
- return -1;
- }
- }
- len = RTE_CACHE_LINE_SIZE;
-
-
-
- prev_min_len = prev_min_ms->len;
- min_len = min_ms->len;
-
- if (min_len >= prev_min_len || prev_min_len - min_len > (unsigned) align) {
- printf("Segments are of wrong lengths!\n");
- return -1;
- }
-
- /* try reserving from a bigger segment */
- if (rte_memzone_reserve_aligned("smallest_offset", len, SOCKET_ID_ANY, 0, align) ==
- NULL) {
- printf("Cannot reserve memory!\n");
- return -1;
- }
-
- /* check if we got memory from correct segment */
- if (min_ms->len != min_len && prev_min_ms->len != (prev_min_len - len)) {
- printf("Reserved memory from segment with smaller offset!\n");
- return -1;
- }
-
- return 0;
-}
-
-static int
-test_memzone_reserve_remainder(void)
-{
- const struct rte_memzone *mz1, *mz2;
- const struct rte_memseg *ms, *min_ms = NULL;
- size_t min_len;
- const struct rte_config *config;
- int i, align;
-
- min_len = 0;
- align = RTE_CACHE_LINE_SIZE;
-
- config = rte_eal_get_configuration();
-
- /* find minimum free contiguous length */
- for (i = 0; i < RTE_MAX_MEMSEG; i++) {
- ms = &config->mem_config->free_memseg[i];
-
- if (ms->addr == NULL)
- break;
- if (ms->len == 0)
- continue;
-
- if (min_len == 0 || ms->len < min_len) {
- min_len = ms->len;
- min_ms = ms;
-
- /* find maximum alignment this segment is able to hold */
- align = RTE_CACHE_LINE_SIZE;
- while ((ms->addr_64 & (align-1)) == 0) {
- align <<= 1;
- }
- }
- }
-
- if (min_ms == NULL) {
- printf("Minimal sized segment not found!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with alignment - this should not affect our
- * memseg, the memory will be taken from a different one.
- */
- mz1 = rte_memzone_reserve_aligned("reserve_remainder_1", min_len,
- SOCKET_ID_ANY, 0, align);
- if (mz1 == NULL) {
- printf("Failed to reserve %zu bytes aligned on %i bytes\n", min_len,
- align);
- return -1;
- }
- if (min_ms->len != min_len) {
- printf("Memseg memory should not have been reserved!\n");
- return -1;
- }
-
- /* try reserving min_len bytes with less alignment - this should fill up
- * the segment.
- */
- mz2 = rte_memzone_reserve("reserve_remainder_2", min_len,
- SOCKET_ID_ANY, 0);
- if (mz2 == NULL) {
- printf("Failed to reserve %zu bytes\n", min_len);
- return -1;
- }
- if (min_ms->len != 0) {
- printf("Memseg memory should have been reserved!\n");
- return -1;
- }
-
return 0;
}
@@ -1125,14 +767,6 @@ test_memzone(void)
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
- printf("test reserving memory in smallest segments\n");
- if (test_memzone_reserve_memory_in_smallest_segment() < 0)
- return -1;
-
- printf("test reserving memory in segments with smallest offsets\n");
- if (test_memzone_reserve_memory_with_smallest_offset() < 0)
- return -1;
-
printf("test memzone_reserve flags\n");
if (test_memzone_reserve_flags() < 0)
return -1;
@@ -1149,10 +783,6 @@ test_memzone(void)
if (test_memzone_invalid_alignment() < 0)
return -1;
- printf("test reserving amounts of memory equal to segment's length\n");
- if (test_memzone_reserve_remainder() < 0)
- return -1;
-
printf("test reserving the largest size memzone possible\n");
if (test_memzone_reserve_max() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (2 preceding siblings ...)
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
` (5 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
During initializaio malloc sets all available memory as part of the heaps.
CONFIG_RTE_MALLOC_MEMZONE_SIZE was used to specify the default memory
block size to expand the heap. The option is not used/relevant anymore,
so we remove it.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
config/common_bsdapp | 1 -
config/common_linuxapp | 1 -
2 files changed, 2 deletions(-)
diff --git a/config/common_bsdapp b/config/common_bsdapp
index 5bb7f55..4e505bf 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -107,7 +107,6 @@ CONFIG_RTE_LOG_HISTORY=256
CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# FreeBSD contiguous memory driver settings
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 7b57044..579a5d7 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -110,7 +110,6 @@ CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
CONFIG_RTE_EAL_IGB_UIO=y
CONFIG_RTE_EAL_VFIO=y
CONFIG_RTE_MALLOC_DEBUG=n
-CONFIG_RTE_MALLOC_MEMZONE_SIZE=11M
#
# Special configurations in PCI Config Space for high performance
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 5/9] eal: remove free_memseg and references to it
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (3 preceding siblings ...)
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
` (4 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
Remove free_memseg field from internal mem config structure as it is
not used anymore.
Also remove code in ivshmem that was setting up free_memseg on init.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/common/include/rte_eal_memconfig.h | 3 ---
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 9 ---------
2 files changed, 12 deletions(-)
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 055212a..7de906b 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -73,9 +73,6 @@ struct rte_mem_config {
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
- /* Runtime Physmem descriptors - NOT USED */
- struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
-
struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
/* Heaps of Malloc per socket */
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index 2deaeb7..facfb80 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -725,15 +725,6 @@ map_all_segments(void)
* expect memsegs to be empty */
memcpy(&mcfg->memseg[i], &ms,
sizeof(struct rte_memseg));
- memcpy(&mcfg->free_memseg[i], &ms,
- sizeof(struct rte_memseg));
-
-
- /* adjust the free_memseg so that there's no free space left */
- mcfg->free_memseg[i].ioremap_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].phys_addr += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].addr_64 += mcfg->free_memseg[i].len;
- mcfg->free_memseg[i].len = 0;
close(fd);
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 6/9] eal: new rte_memzone_free
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (4 preceding siblings ...)
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
` (3 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
Implement rte_memzone_free which, as its name implies, would free a
memzone.
Currently memzone are tracked in an array and cannot be free.
To be able to reuse the same array to track memzones, we have to
change how we keep track of reserved memzones.
With this patch, any memzone with addr NULL is not used, so we also need
to change how we look for the next memzone entry free.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
lib/librte_eal/bsdapp/eal/rte_eal_version.map | 6 ++
lib/librte_eal/common/eal_common_memzone.c | 68 ++++++++++++++++++++++-
lib/librte_eal/common/include/rte_eal_memconfig.h | 2 +-
lib/librte_eal/common/include/rte_memzone.h | 11 ++++
lib/librte_eal/linuxapp/eal/eal_ivshmem.c | 8 +--
lib/librte_eal/linuxapp/eal/rte_eal_version.map | 6 ++
6 files changed, 93 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
diff --git a/lib/librte_eal/common/eal_common_memzone.c b/lib/librte_eal/common/eal_common_memzone.c
index 31bf6d8..7b1d77e 100644
--- a/lib/librte_eal/common/eal_common_memzone.c
+++ b/lib/librte_eal/common/eal_common_memzone.c
@@ -77,6 +77,23 @@ memzone_lookup_thread_unsafe(const char *name)
return NULL;
}
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+ struct rte_mem_config *mcfg;
+ unsigned i = 0;
+
+ /* get pointer to global configuration */
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+ if (mcfg->memzone[i].addr == NULL)
+ return &mcfg->memzone[i];
+ }
+
+ return NULL;
+}
+
/* This function will return the greatest free block if a heap has been
* specified. If no heap has been specified, it will return the heap and
* length of the greatest free block available in all heaps */
@@ -117,7 +134,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
mcfg = rte_eal_get_configuration()->mem_config;
/* no more room in config */
- if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
rte_errno = ENOSPC;
return NULL;
@@ -206,7 +223,16 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
/* fill the zone in config */
- struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+ struct rte_memzone *mz = get_next_free_memzone();
+
+ if (mz == NULL) {
+ RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
+ "in config!\n", __func__);
+ rte_errno = ENOSPC;
+ return NULL;
+ }
+
+ mcfg->memzone_cnt++;
snprintf(mz->name, sizeof(mz->name), "%s", name);
mz->phys_addr = rte_malloc_virt2phy(mz_addr);
mz->addr = mz_addr;
@@ -277,6 +303,42 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
flags, RTE_CACHE_LINE_SIZE, 0);
}
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+ struct rte_mem_config *mcfg;
+ int ret = 0;
+ void *addr;
+ unsigned idx;
+
+ if (mz == NULL)
+ return -EINVAL;
+
+ mcfg = rte_eal_get_configuration()->mem_config;
+
+ rte_rwlock_write_lock(&mcfg->mlock);
+
+ idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+ idx = idx / sizeof(struct rte_memzone);
+
+ addr = mcfg->memzone[idx].addr;
+ if (addr == NULL)
+ ret = -EINVAL;
+ else if (mcfg->memzone_cnt == 0) {
+ rte_panic("%s(): memzone address not NULL but memzone_cnt is 0!\n",
+ __func__);
+ } else {
+ memset(&mcfg->memzone[idx], 0, sizeof(mcfg->memzone[idx]));
+ mcfg->memzone_cnt--;
+ }
+
+ rte_rwlock_write_unlock(&mcfg->mlock);
+
+ rte_free(addr);
+
+ return ret;
+}
+
/*
* Lookup for the memzone identified by the given name
*/
@@ -349,7 +411,7 @@ rte_eal_memzone_init(void)
rte_rwlock_write_lock(&mcfg->mlock);
/* delete all zones */
- mcfg->memzone_idx = 0;
+ mcfg->memzone_cnt = 0;
memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
rte_rwlock_write_unlock(&mcfg->mlock);
diff --git a/lib/librte_eal/common/include/rte_eal_memconfig.h b/lib/librte_eal/common/include/rte_eal_memconfig.h
index 7de906b..2b5e0b1 100644
--- a/lib/librte_eal/common/include/rte_eal_memconfig.h
+++ b/lib/librte_eal/common/include/rte_eal_memconfig.h
@@ -67,7 +67,7 @@ struct rte_mem_config {
rte_rwlock_t qlock; /**< used for tailq operation for thread safe. */
rte_rwlock_t mplock; /**< only used by mempool LIB for thread-safe. */
- uint32_t memzone_idx; /**< Index of memzone */
+ uint32_t memzone_cnt; /**< Number of allocated memzones */
/* memory segments and zones */
struct rte_memseg memseg[RTE_MAX_MEMSEG]; /**< Physmem descriptors. */
diff --git a/lib/librte_eal/common/include/rte_memzone.h b/lib/librte_eal/common/include/rte_memzone.h
index de5ae55..38e5f5b 100644
--- a/lib/librte_eal/common/include/rte_memzone.h
+++ b/lib/librte_eal/common/include/rte_memzone.h
@@ -256,6 +256,17 @@ const struct rte_memzone *rte_memzone_reserve_bounded(const char *name,
unsigned flags, unsigned align, unsigned bound);
/**
+ * Free a memzone.
+ *
+ * @param mz
+ * A pointer to the memzone
+ * @return
+ * -EINVAL - invalid parameter
+ * 0 - success
+ */
+int rte_memzone_free(const struct rte_memzone *mz);
+
+/**
* Lookup for a memzone.
*
* Get a pointer to a descriptor of an already reserved memory
diff --git a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
index facfb80..589019b 100644
--- a/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
+++ b/lib/librte_eal/linuxapp/eal/eal_ivshmem.c
@@ -768,12 +768,12 @@ rte_eal_ivshmem_obj_init(void)
seg = &ivshmem_config->segment[i];
/* add memzone */
- if (mcfg->memzone_idx == RTE_MAX_MEMZONE) {
+ if (mcfg->memzone_cnt == RTE_MAX_MEMZONE) {
RTE_LOG(ERR, EAL, "No more memory zones available!\n");
return -1;
}
- idx = mcfg->memzone_idx;
+ idx = mcfg->memzone_cnt;
RTE_LOG(DEBUG, EAL, "Found memzone: '%s' at %p (len 0x%" PRIx64 ")\n",
seg->entry.mz.name, seg->entry.mz.addr, seg->entry.mz.len);
@@ -796,13 +796,13 @@ rte_eal_ivshmem_obj_init(void)
}
}
- mcfg->memzone_idx++;
+ mcfg->memzone_cnt++;
}
rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
/* find rings */
- for (i = 0; i < mcfg->memzone_idx; i++) {
+ for (i = 0; i < mcfg->memzone_cnt; i++) {
mz = &mcfg->memzone[i];
/* check if memzone has a ring prefix */
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c107b05..e537b42 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -111,3 +111,9 @@ DPDK_2.0 {
local: *;
};
+
+DPDK_2.1 {
+ global:
+
+ rte_memzone_free;
+} DPDK_2.0;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 7/9] app/test: rte_memzone_free unit test
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (5 preceding siblings ...)
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
` (2 subsequent siblings)
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
Add new unit test for rte_memzone_free API.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
app/test/test_memzone.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/app/test/test_memzone.c b/app/test/test_memzone.c
index 6934eee..c37f950 100644
--- a/app/test/test_memzone.c
+++ b/app/test/test_memzone.c
@@ -432,7 +432,6 @@ test_memzone_reserve_max(void)
printf("Expected size = %zu, actual size = %zu\n", maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -472,7 +471,6 @@ test_memzone_reserve_max_aligned(void)
maxlen, mz->len);
rte_dump_physmem_layout(stdout);
rte_memzone_dump(stdout);
-
return -1;
}
return 0;
@@ -684,6 +682,82 @@ test_memzone_bounded(void)
}
static int
+test_memzone_free(void)
+{
+ const struct rte_memzone *mz[RTE_MAX_MEMZONE];
+ int i;
+ char name[20];
+
+ mz[0] = rte_memzone_reserve("tempzone0", 2000, SOCKET_ID_ANY, 0);
+ mz[1] = rte_memzone_reserve("tempzone1", 4000, SOCKET_ID_ANY, 0);
+
+ if (mz[0] > mz[1])
+ return -1;
+ if (!rte_memzone_lookup("tempzone0"))
+ return -1;
+ if (!rte_memzone_lookup("tempzone1"))
+ return -1;
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone0")) {
+ printf("Found previously free memzone - tempzone0\n");
+ return -1;
+ }
+ mz[2] = rte_memzone_reserve("tempzone2", 2000, SOCKET_ID_ANY, 0);
+
+ if (mz[2] > mz[1]) {
+ printf("tempzone2 should have gotten the free entry from tempzone0\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[2])) {
+ printf("Fail memzone free - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone2")) {
+ printf("Found previously free memzone - tempzone2\n");
+ return -1;
+ }
+ if (rte_memzone_free(mz[1])) {
+ printf("Fail memzone free - tempzone1\n");
+ return -1;
+ }
+ if (rte_memzone_lookup("tempzone1")) {
+ printf("Found previously free memzone - tempzone1\n");
+ return -1;
+ }
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "tempzone%u", i);
+ mz[i] = rte_memzone_reserve(name, 1, SOCKET_ID_ANY, 0);
+ } while (mz[i++] != NULL);
+
+ if (rte_memzone_free(mz[0])) {
+ printf("Fail memzone free - tempzone0\n");
+ return -1;
+ }
+ mz[0] = rte_memzone_reserve("tempzone0new", 0, SOCKET_ID_ANY, 0);
+
+ if (mz[0] == NULL) {
+ printf("Fail to create memzone - tempzone0new - when MAX memzones were "
+ "created and one was free\n");
+ return -1;
+ }
+
+ for (i = i - 2; i >= 0; i--) {
+ if (rte_memzone_free(mz[i])) {
+ printf("Fail memzone free - tempzone%d\n", i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
test_memzone(void)
{
const struct rte_memzone *memzone1;
@@ -763,6 +837,10 @@ test_memzone(void)
if (mz != NULL)
return -1;
+ printf("test free memzone\n");
+ if (test_memzone_free() < 0)
+ return -1;
+
printf("test reserving memzone with bigger size than the maximum\n");
if (test_memzone_reserving_zone_size_bigger_than_the_maximum() < 0)
return -1;
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 8/9] doc: announce ABI change of librte_malloc
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (6 preceding siblings ...)
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-07-16 12:05 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Thomas Monjalon
9 siblings, 0 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
Announce the creation of dummy malloc library for 2.1 and removal of
such library, now integrated in librte_eal, for 2.2 release.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/rel_notes/abi.rst | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/doc/guides/rel_notes/abi.rst b/doc/guides/rel_notes/abi.rst
index 931e785..76e0ae2 100644
--- a/doc/guides/rel_notes/abi.rst
+++ b/doc/guides/rel_notes/abi.rst
@@ -16,7 +16,6 @@ Examples of Deprecation Notices
Deprecation Notices
-------------------
-
* Significant ABI changes are planned for struct rte_eth_dev to support up to
1024 queues per port. This change will be in release 2.2.
There is no backward compatibility planned from release 2.2.
@@ -24,3 +23,8 @@ Deprecation Notices
* The Macros RTE_HASH_BUCKET_ENTRIES_MAX and RTE_HASH_KEY_LENGTH_MAX are
deprecated and will be removed with version 2.2.
+
+* librte_malloc library has been integrated into librte_eal. The 2.1 release
+ creates a dummy/empty malloc library to fulfill binaries with dynamic linking
+ dependencies on librte_malloc.so. Such dummy library will not be created from
+ release 2.2 so binaries will need to be rebuilt.
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v10 9/9] doc: update malloc documentation
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (7 preceding siblings ...)
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
@ 2015-07-15 16:32 ` Sergio Gonzalez Monroy
2015-07-16 7:37 ` [dpdk-dev] [PATCH v11] " Sergio Gonzalez Monroy
2015-07-16 12:05 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Thomas Monjalon
9 siblings, 1 reply; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-15 16:32 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 0 bytes
doc/guides/prog_guide/img/malloc_heap.svg | 1018 +++++++++++++++++++++++
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 ------
doc/guides/prog_guide/overview.rst | 11 +-
6 files changed, 1239 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/img/malloc_heap.png
create mode 100755 doc/guides/prog_guide/img/malloc_heap.svg
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
deleted file mode 100644
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 81329
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%0AJ1PuixJX??D}PX8AKL
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
diff --git a/doc/guides/prog_guide/img/malloc_heap.svg b/doc/guides/prog_guide/img/malloc_heap.svg
new file mode 100755
index 0000000..fb572e8
--- /dev/null
+++ b/doc/guides/prog_guide/img/malloc_heap.svg
@@ -0,0 +1,1018 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ width="983.76233"
+ height="643.91644"
+ sodipodi:docname="malloc_heap_svg.svg">
+ <metadata
+ id="metadata2991">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs2989">
+ <marker
+ inkscape:stockid="Arrow2Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mstart"
+ style="overflow:visible">
+ <path
+ id="path4265"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(0.6,0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path4259"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend"
+ style="overflow:visible">
+ <path
+ id="path4268"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path4262"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path4244"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-1"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-8"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-9"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-6"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mstart-7"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4265-8"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(0.6,0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-8"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-2"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-2"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-0"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mstart-5"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4265-7"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(0.6,0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-5"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#30ff00"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1139"
+ id="namedview2987"
+ showgrid="false"
+ inkscape:zoom="0.8"
+ inkscape:cx="346.31962"
+ inkscape:cy="474.02351"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer4"
+ borderlayer="false"
+ fit-margin-top="-100.6"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ showborder="true"
+ inkscape:showpageshadow="false" />
+ <g
+ inkscape:groupmode="layer"
+ id="layer4"
+ inkscape:label="bg"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#d1d1d1;fill-opacity:1;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect13505-6"
+ width="98.575218"
+ height="70.808708"
+ x="328.8374"
+ y="317.09564" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="boxes"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect2996-1"
+ width="187.88171"
+ height="52.881706"
+ x="75.764778"
+ y="5.5253706" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7"
+ width="634.0592"
+ height="73.027374"
+ x="60.830574"
+ y="130.24477" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.02648067;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-4"
+ width="635.80048"
+ height="74.768661"
+ x="62.169655"
+ y="315.43158" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.85834479;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-0"
+ width="886.87543"
+ height="106.64049"
+ x="-48.78373"
+ y="540.24988" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.13159013;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6.26318017, 3.13159009;stroke-dashoffset:0;display:inline"
+ id="rect2996-1-5"
+ width="223.0157"
+ height="109.20289"
+ x="409.68008"
+ y="420.63235" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.90856051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:5.81712091, 2.90856046;stroke-dashoffset:0;display:inline"
+ id="rect2996-1-5-4"
+ width="191.98872"
+ height="109.42592"
+ x="644.63062"
+ y="419.66205" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.08755708;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.17511403, 2.08755702;stroke-dashoffset:0;display:inline"
+ id="rect2996-1-5-4-6"
+ width="154.05972"
+ height="70.246925"
+ x="678.59509"
+ y="214.87654" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="blue headers"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#749aba;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.85091281;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9"
+ width="16.994427"
+ height="73.79715"
+ x="59.561817"
+ y="129.601" />
+ <rect
+ style="fill:#749aba;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.83000004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-4"
+ width="17.015339"
+ height="72.050293"
+ x="384.61731"
+ y="130.22485" />
+ <rect
+ style="fill:#749aba;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.86642051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-8"
+ width="16.978918"
+ height="75.107468"
+ x="261.76944"
+ y="315.16946" />
+ <rect
+ style="fill:#749aba;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.36914372;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-82"
+ width="48.412117"
+ height="14.17484"
+ x="-42.956367"
+ y="549.14984" />
+ <rect
+ style="fill:#97ffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.83000004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-4-1"
+ width="17.015339"
+ height="72.050293"
+ x="241.39912"
+ y="131.17525" />
+ <rect
+ style="fill:#97ffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.36399999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-4-1-3"
+ width="16.981569"
+ height="74.882637"
+ x="568.40881"
+ y="315.33447" />
+ <rect
+ style="fill:#97ffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.95599997;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-4-1-3-7"
+ width="49.319912"
+ height="12.752681"
+ x="-43.016232"
+ y="595.7439" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer5"
+ inkscape:label="red headers"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.83000004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45"
+ width="17.015339"
+ height="72.050293"
+ x="501.49307"
+ y="130.29137" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.84049058;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-5"
+ width="17.004848"
+ height="72.923683"
+ x="678.04279"
+ y="130.29662" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.85091281;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-1"
+ width="16.994427"
+ height="73.79715"
+ x="681.8158"
+ y="316.14957" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.86126781;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7"
+ width="16.984072"
+ height="74.670677"
+ x="500.62485"
+ y="315.92252" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.82472873;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-11"
+ width="17.020611"
+ height="71.613625"
+ x="175.33748"
+ y="131.40486" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.86642051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-52"
+ width="16.978918"
+ height="75.107468"
+ x="62.221222"
+ y="315.0412" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.39574718;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-76"
+ width="48.805244"
+ height="14.612387"
+ x="-42.996674"
+ y="572.61749" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer9"
+ inkscape:label="unused space"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#dddddd;fill-opacity:1;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect13505"
+ width="98.575218"
+ height="70.808708"
+ x="402.22061"
+ y="131.06841" />
+ <rect
+ style="fill:#dddddd;fill-opacity:1;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect13505-8"
+ width="96.700218"
+ height="70.808708"
+ x="77.587402"
+ y="131.47064" />
+ <rect
+ style="fill:#dddddd;fill-opacity:1;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
+ id="rect13505-5"
+ width="220.21585"
+ height="72.839958"
+ x="279.26709"
+ y="316.08002" />
+ <rect
+ style="fill:#dddddd;fill-opacity:1;stroke:#000000;stroke-width:1.12016988;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
+ id="rect13505-59"
+ width="51.879829"
+ height="15.10388"
+ x="445.6301"
+ y="550.76691" />
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:1.12016988;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
+ id="rect13505-59-3"
+ width="51.879829"
+ height="15.10388"
+ x="445.62964"
+ y="574.00262" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer8"
+ inkscape:label="pad headers"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#fffec5;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7-3"
+ width="49.88493"
+ height="73.447571"
+ x="518.21405"
+ y="316.16635" />
+ <rect
+ style="fill:#fffec5;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.86126781;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7-3-2"
+ width="16.98407"
+ height="74.670677"
+ x="245.17551"
+ y="315.48059" />
+ <rect
+ style="fill:#fffec5;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.02099991;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7-3-4"
+ width="49.474121"
+ height="72.084908"
+ x="193.07074"
+ y="130.93698" />
+ <rect
+ style="fill:#fffec5;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7-3-6"
+ width="51.75993"
+ height="14.072571"
+ x="445.05756"
+ y="596.40125" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer6"
+ inkscape:label="arrows"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:9, 9;stroke-dashoffset:0;marker-mid:none;marker-end:url(#Arrow2Mend)"
+ d="m 262.87951,51.152779 c 0,0 148.12631,-3.276651 187.01718,76.272861"
+ id="path3973"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend)"
+ d="m 681.9161,128.72302 c -22.09709,-49.497478 -148.13393,-45.873109 -179.42835,0"
+ id="path3988"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="M 386.69903,129.58525 C 361.95029,80.971668 231.48641,62.20327 177.21864,130.46914"
+ id="path3990"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend)"
+ d="m 60.546017,172.89554 c 0,0 -32.703692,23.86486 -60.10407166,-3.53553"
+ id="path3992"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="m 176.82896,203.22242 c -47.24941,74.32926 -107.438064,49.90804 -116.0476,3.53553"
+ id="path4035"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="m 502.04581,203.43962 c -25.63262,33.58757 -82.31601,45.11485 -116.67261,2.65165"
+ id="path4037"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="M 763.23339,214.04621 C 748.83403,184.37018 738.54555,166.795 699.15183,161.8971"
+ id="path4039"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-mid:none;marker-end:url(#Arrow2Mend-1)"
+ d="m 769.42057,285.19885 c -0.88389,83.96892 -68.50098,75.57203 -68.50098,75.57203"
+ id="path4041"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="M 682.35804,313.04117 C 652.306,280.33749 539.16892,270.61477 501.16193,313.92506"
+ id="path4043"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:9, 9;stroke-dashoffset:0;marker-end:url(#Arrow2Mend)"
+ d="m 415.42523,202.55574 c 0,36.23922 -4.41941,88.38835 -35.35533,109.60155"
+ id="path4045"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:9, 9;stroke-dashoffset:0;marker-end:url(#Arrow2Mend)"
+ d="M 375.65048,315.69282 C 336.75961,232.60777 166.1701,311.27341 143.18912,205.20739"
+ id="path4047"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="M 263.39727,315.69282 C 245.7196,288.29244 86.62058,275.91807 62.755726,313.04117"
+ id="path4051"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend)"
+ d="m 61.790091,352.05822 c -25.819377,20.1091 -49.573204,20.1091 -61.96650422,1.43636"
+ id="path4053"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:7.65, 7.65;stroke-dashoffset:0;marker-end:url(#Arrow2Mend)"
+ d="m 448.12892,630.25126 48.61359,0"
+ id="path5241"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.09116507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend);display:inline"
+ d="m -39.741559,626.33548 c 10.599699,-0.12345 25.528414,-0.12564 43.719789,-0.81161"
+ id="path4053-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="m 499.39416,389.93904 c -46.84583,17.67767 -206.82873,31.8198 -238.64854,1.76776"
+ id="path13236"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1);display:inline"
+ d="m 502.12201,419.58783 c 2.37436,-10.40132 1.73096,-5.65101 4.38262,-26.86421"
+ id="path4043-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend-1);display:inline"
+ d="m 517.94842,353.38466 c 19.7099,0 43.91577,-0.61421 66.57012,-0.61421"
+ id="path4043-4-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend-1);display:inline"
+ d="m 501.71494,363.4321 c 19.7099,0 157.04077,-0.61421 179.69512,-0.61421"
+ id="path4043-4-3-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1);display:inline"
+ d="M 728.67747,419.79091 C 702.92683,395.63959 592.90843,427.2649 577.43509,389.1767"
+ id="path4043-4-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend-1);display:inline"
+ d="m 60.975741,169.05711 c 19.709901,0 90.307569,-0.61421 112.961919,-0.61421"
+ id="path4043-4-3-9-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer7"
+ inkscape:label="text"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="90.732231"
+ y="36.767765"
+ id="text10506"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508"
+ x="90.732231"
+ y="36.767765"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">struct malloc_heap</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="580.66718"
+ y="107.47876"
+ id="text10506-2"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1"
+ x="580.66718"
+ y="107.47876"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="438.12686"
+ y="223.50792"
+ id="text10506-2-5"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-7"
+ x="438.12686"
+ y="223.50792"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="581.31598"
+ y="298.638"
+ id="text10506-2-61"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-89"
+ x="581.31598"
+ y="298.638"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="274.6084"
+ y="99.764236"
+ id="text10506-2-2"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-79"
+ x="274.6084"
+ y="99.764236"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="301.12491"
+ y="423.26556"
+ id="text10506-2-54"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-3"
+ x="301.12491"
+ y="423.26556"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="133.18704"
+ y="303.94128"
+ id="text10506-2-1"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-2"
+ x="133.18704"
+ y="303.94128"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="16.340637"
+ y="561.27954"
+ id="text10506-2-3"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34"
+ x="16.340637"
+ y="561.27954"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Free element header(struct malloc_elem, state = FREE)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="16.996887"
+ y="583.24792"
+ id="text10506-2-3-1"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1"
+ x="16.996887"
+ y="583.24792"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Used element header(struct malloc_elem, state = BUSY)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="108.84206"
+ y="161.39597"
+ id="text10506-2-6-8"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-7"
+ x="108.84206"
+ y="161.39597"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">size</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="62.299515"
+ y="119.27286"
+ id="text10506-2-6-4"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-2"
+ x="62.299515"
+ y="119.27286"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Memseg 0</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="63.905106"
+ y="406.73242"
+ id="text10506-2-6-4-7"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-2-7"
+ x="63.905106"
+ y="406.73242"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Memseg 1</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="-25.028084"
+ y="192.57199"
+ id="text10506-2-9"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-31"
+ x="-25.028084"
+ y="192.57199"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="-26.795866"
+ y="379.95526"
+ id="text10506-2-98"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-6"
+ x="-26.795866"
+ y="379.95526"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="416.73682"
+ y="269.53305"
+ id="text10506-2-6-5"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-0"
+ x="416.73682"
+ y="269.53305"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">next_free</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="228.00418"
+ y="259.55359"
+ id="text10506-2-6-5-2"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-0-8"
+ x="228.00418"
+ y="259.55359"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">next_free</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="356.16727"
+ y="55.376503"
+ id="text10506-2-6-5-6"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-0-0"
+ x="356.16727"
+ y="55.376503"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">free_head</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="49.218113"
+ y="254.00189"
+ id="text10506-2-9-0"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-31-9"
+ x="49.218113"
+ y="254.00189"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="690.51538"
+ y="236.82936"
+ id="text10506-2-6-0"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-06"
+ x="690.51538"
+ y="236.82936"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Dummy Elements:</tspan><tspan
+ sodipodi:role="line"
+ x="690.51538"
+ y="256.02936"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13581">Size = 0</tspan><tspan
+ sodipodi:role="line"
+ x="690.51538"
+ y="275.22937"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13583">State = BUSY</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="541.03906"
+ y="347.20566"
+ id="text10506-2-6-8-8"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-7-9"
+ x="541.03906"
+ y="347.20566"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">pad</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="16.661926"
+ y="605.21631"
+ id="text10506-2-3-1-4"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-4"
+ x="16.661926"
+ y="605.21631"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Pad element header(struct malloc_elem, state = PAD)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="17.290833"
+ y="627.77881"
+ id="text10506-2-3-1-6"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-0"
+ x="17.290833"
+ y="627.77881"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Generic element pointers</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="429.11118"
+ y="449.84528"
+ id="text10506-2-6-6"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ x="429.11118"
+ y="449.84528"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13711">Malloc element header:</tspan><tspan
+ sodipodi:role="line"
+ x="429.11118"
+ y="469.04529"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13713">state = BUSY</tspan><tspan
+ sodipodi:role="line"
+ x="429.11118"
+ y="488.24527"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13715">size = <size></tspan><tspan
+ sodipodi:role="line"
+ x="429.11118"
+ y="507.44528"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13717">pad = <padsize></tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot13719"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
+ id="flowRegion13721"><rect
+ id="rect13723"
+ width="968.73627"
+ height="188.26718"
+ x="-81.317276"
+ y="460.64972" /></flowRegion><flowPara
+ id="flowPara13725"></flowPara></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="594.30859"
+ y="378.91797"
+ id="text10506-2-6-8-8-1"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-7-9-3"
+ x="594.30859"
+ y="378.91797"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">size</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="505.86865"
+ y="563.34613"
+ id="text10506-2-3-1-6-8"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-0-4"
+ x="505.86865"
+ y="563.34613"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Free / Unallocated data space</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="660.39099"
+ y="449.92532"
+ id="text10506-2-6-6-0"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ x="660.39099"
+ y="449.92532"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan14527">Pad element header:</tspan><tspan
+ sodipodi:role="line"
+ x="660.39099"
+ y="469.12534"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan14531">state = PAD</tspan><tspan
+ sodipodi:role="line"
+ x="660.39099"
+ y="488.32532"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan14533">pad = padsize</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="506.5249"
+ y="584.28369"
+ id="text10506-2-3-1-6-8-7"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-0-4-2"
+ x="506.5249"
+ y="584.28369"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Used / allocated data space</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="506.18994"
+ y="605.30322"
+ id="text10506-2-3-1-6-8-7-0"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-0-4-2-1"
+ x="506.18994"
+ y="605.30322"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Padding / unavailable space</tspan></text>
+ </g>
+</svg>
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 036640c..176f2c2 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* [dpdk-dev] [PATCH v11] doc: update malloc documentation
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
@ 2015-07-16 7:37 ` Sergio Gonzalez Monroy
2015-07-16 10:07 ` Thomas Monjalon
2015-07-16 10:12 ` Thomas Monjalon
0 siblings, 2 replies; 108+ messages in thread
From: Sergio Gonzalez Monroy @ 2015-07-16 7:37 UTC (permalink / raw)
To: dev
Update malloc documentation to reflect new implementation details.
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
---
v11:
- Add copyright and licence to SVG file
doc/guides/prog_guide/env_abstraction_layer.rst | 220 ++++-
doc/guides/prog_guide/img/malloc_heap.png | Bin 81329 -> 0 bytes
doc/guides/prog_guide/img/malloc_heap.svg | 1052 +++++++++++++++++++++++
doc/guides/prog_guide/index.rst | 1 -
doc/guides/prog_guide/malloc_lib.rst | 233 -----
doc/guides/prog_guide/overview.rst | 11 +-
6 files changed, 1273 insertions(+), 244 deletions(-)
delete mode 100644 doc/guides/prog_guide/img/malloc_heap.png
create mode 100755 doc/guides/prog_guide/img/malloc_heap.svg
delete mode 100644 doc/guides/prog_guide/malloc_lib.rst
diff --git a/doc/guides/prog_guide/env_abstraction_layer.rst b/doc/guides/prog_guide/env_abstraction_layer.rst
index 25eb281..cd4d666 100644
--- a/doc/guides/prog_guide/env_abstraction_layer.rst
+++ b/doc/guides/prog_guide/env_abstraction_layer.rst
@@ -116,7 +116,6 @@ The physical address of the reserved memory for that memory zone is also returne
.. note::
Memory reservations done using the APIs provided by the rte_malloc library are also backed by pages from the hugetlbfs filesystem.
- However, physical address information is not available for the blocks of memory allocated in this way.
Xen Dom0 support without hugetbls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -366,3 +365,222 @@ We expect only 50% of CPU spend on packet IO.
echo 50000 > pkt_io/cpu.cfs_quota_us
+Malloc
+------
+
+The EAL provides a malloc API to allocate any-sized memory.
+
+The objective of this API is to provide malloc-like functions to allow
+allocation from hugepage memory and to facilitate application porting.
+The *DPDK API Reference* manual describes the available functions.
+
+Typically, these kinds of allocations should not be done in data plane
+processing because they are slower than pool-based allocation and make
+use of locks within the allocation and free paths.
+However, they can be used in configuration code.
+
+Refer to the rte_malloc() function description in the *DPDK API Reference*
+manual for more information.
+
+Cookies
+~~~~~~~
+
+When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains
+overwrite protection fields to help identify buffer overflows.
+
+Alignment and NUMA Constraints
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The rte_malloc() takes an align argument that can be used to request a memory
+area that is aligned on a multiple of this value (which must be a power of two).
+
+On systems with NUMA support, a call to the rte_malloc() function will return
+memory that has been allocated on the NUMA socket of the core which made the call.
+A set of APIs is also provided, to allow memory to be explicitly allocated on a
+NUMA socket directly, or by allocated on the NUMA socket where another core is
+located, in the case where the memory is to be used by a logical core other than
+on the one doing the memory allocation.
+
+Use Cases
+~~~~~~~~~
+
+This API is meant to be used by an application that requires malloc-like
+functions at initialization time.
+
+For allocating/freeing data at runtime, in the fast-path of an application,
+the memory pool library should be used instead.
+
+Internal Implementation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+There are two data structure types used internally in the malloc library:
+
+* struct malloc_heap - used to track free space on a per-socket basis
+
+* struct malloc_elem - the basic element of allocation and free-space
+ tracking inside the library.
+
+Structure: malloc_heap
+""""""""""""""""""""""
+
+The malloc_heap structure is used to manage free space on a per-socket basis.
+Internally, there is one heap structure per NUMA node, which allows us to
+allocate memory to a thread based on the NUMA node on which this thread runs.
+While this does not guarantee that the memory will be used on that NUMA node,
+it is no worse than a scheme where the memory is always allocated on a fixed
+or random node.
+
+The key fields of the heap structure and their function are described below
+(see also diagram above):
+
+* lock - the lock field is needed to synchronize access to the heap.
+ Given that the free space in the heap is tracked using a linked list,
+ we need a lock to prevent two threads manipulating the list at the same time.
+
+* free_head - this points to the first element in the list of free nodes for
+ this malloc heap.
+
+.. note::
+
+ The malloc_heap structure does not keep track of in-use blocks of memory,
+ since these are never touched except when they are to be freed again -
+ at which point the pointer to the block is an input to the free() function.
+
+.. _figure_malloc_heap:
+
+.. figure:: img/malloc_heap.*
+
+ Example of a malloc heap and malloc elements within the malloc library
+
+
+.. _malloc_elem:
+
+Structure: malloc_elem
+""""""""""""""""""""""
+
+The malloc_elem structure is used as a generic header structure for various
+blocks of memory.
+It is used in three different ways - all shown in the diagram above:
+
+#. As a header on a block of free or allocated memory - normal case
+
+#. As a padding header inside a block of memory
+
+#. As an end-of-memseg marker
+
+The most important fields in the structure and how they are used are described below.
+
+.. note::
+
+ If the usage of a particular field in one of the above three usages is not
+ described, the field can be assumed to have an undefined value in that
+ situation, for example, for padding headers only the "state" and "pad"
+ fields have valid values.
+
+* heap - this pointer is a reference back to the heap structure from which
+ this block was allocated.
+ It is used for normal memory blocks when they are being freed, to add the
+ newly-freed block to the heap's free-list.
+
+* prev - this pointer points to the header element/block in the memseg
+ immediately behind the current one. When freeing a block, this pointer is
+ used to reference the previous block to check if that block is also free.
+ If so, then the two free blocks are merged to form a single larger block.
+
+* next_free - this pointer is used to chain the free-list of unallocated
+ memory blocks together.
+ It is only used in normal memory blocks; on ``malloc()`` to find a suitable
+ free block to allocate and on ``free()`` to add the newly freed element to
+ the free-list.
+
+* state - This field can have one of three values: ``FREE``, ``BUSY`` or
+ ``PAD``.
+ The former two are to indicate the allocation state of a normal memory block
+ and the latter is to indicate that the element structure is a dummy structure
+ at the end of the start-of-block padding, i.e. where the start of the data
+ within a block is not at the start of the block itself, due to alignment
+ constraints.
+ In that case, the pad header is used to locate the actual malloc element
+ header for the block.
+ For the end-of-memseg structure, this is always a ``BUSY`` value, which
+ ensures that no element, on being freed, searches beyond the end of the
+ memseg for other blocks to merge with into a larger free area.
+
+* pad - this holds the length of the padding present at the start of the block.
+ In the case of a normal block header, it is added to the address of the end
+ of the header to give the address of the start of the data area, i.e. the
+ value passed back to the application on a malloc.
+ Within a dummy header inside the padding, this same value is stored, and is
+ subtracted from the address of the dummy header to yield the address of the
+ actual block header.
+
+* size - the size of the data block, including the header itself.
+ For end-of-memseg structures, this size is given as zero, though it is never
+ actually checked.
+ For normal blocks which are being freed, this size value is used in place of
+ a "next" pointer to identify the location of the next block of memory that
+ in the case of being ``FREE``, the two free blocks can be merged into one.
+
+Memory Allocation
+^^^^^^^^^^^^^^^^^
+
+On EAL initialisation, all memsegs are setup as part of the malloc heap.
+This setup involves placing a dummy structure at the end with ``BUSY`` state,
+which may contain a sentinel value if ``CONFIG_RTE_MALLOC_DEBUG`` is enabled,
+and a proper :ref:`element header<malloc_elem>` with ``FREE`` at the start
+for each memseg.
+The ``FREE`` element is then added to the ``free_list`` for the malloc heap.
+
+When an application makes a call to a malloc-like function, the malloc function
+will first index the ``lcore_config`` structure for the calling thread, and
+determine the NUMA node of that thread.
+The NUMA node is used to index the array of ``malloc_heap`` structures which is
+passed as a parameter to the ``heap_alloc()`` function, along with the
+requested size, type, alignment and boundary parameters.
+
+The ``heap_alloc()`` function will scan the free_list of the heap, and attempt
+to find a free block suitable for storing data of the requested size, with the
+requested alignment and boundary constraints.
+
+When a suitable free element has been identified, the pointer to be returned
+to the user is calculated.
+The cache-line of memory immediately preceding this pointer is filled with a
+struct malloc_elem header.
+Because of alignment and boundary constraints, there could be free space at
+the start and/or end of the element, resulting in the following behavior:
+
+#. Check for trailing space.
+ If the trailing space is big enough, i.e. > 128 bytes, then the free element
+ is split.
+ If it is not, then we just ignore it (wasted space).
+
+#. Check for space at the start of the element.
+ If the space at the start is small, i.e. <=128 bytes, then a pad header is
+ used, and the remaining space is wasted.
+ If, however, the remaining space is greater, then the free element is split.
+
+The advantage of allocating the memory from the end of the existing element is
+that no adjustment of the free list needs to take place - the existing element
+on the free list just has its size pointer adjusted, and the following element
+has its "prev" pointer redirected to the newly created element.
+
+Freeing Memory
+^^^^^^^^^^^^^^
+
+To free an area of memory, the pointer to the start of the data area is passed
+to the free function.
+The size of the ``malloc_elem`` structure is subtracted from this pointer to get
+the element header for the block.
+If this header is of type ``PAD`` then the pad length is further subtracted from
+the pointer to get the proper element header for the entire block.
+
+From this element header, we get pointers to the heap from which the block was
+allocated and to where it must be freed, as well as the pointer to the previous
+element, and via the size field, we can calculate the pointer to the next element.
+These next and previous elements are then checked to see if they are also
+``FREE``, and if so, they are merged with the current element.
+This means that we can never have two ``FREE`` memory blocks adjacent to one
+another, as they are always merged into a single block.
diff --git a/doc/guides/prog_guide/img/malloc_heap.png b/doc/guides/prog_guide/img/malloc_heap.png
deleted file mode 100644
index 4449fda0dd0b7688b32854e8e32f8c45c505f5f9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 81329
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%0AJ1PuixJX??D}PX8AKL
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
diff --git a/doc/guides/prog_guide/img/malloc_heap.svg b/doc/guides/prog_guide/img/malloc_heap.svg
new file mode 100755
index 0000000..d6bcc84
--- /dev/null
+++ b/doc/guides/prog_guide/img/malloc_heap.svg
@@ -0,0 +1,1052 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<!--
+# Copyright (c) <2015>, Intel Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# - Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# - Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ width="983.76233"
+ height="643.91644"
+ sodipodi:docname="malloc_heap_svg.svg">
+ <metadata
+ id="metadata2991">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs2989">
+ <marker
+ inkscape:stockid="Arrow2Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mstart"
+ style="overflow:visible">
+ <path
+ id="path4265"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(0.6,0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path4259"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend"
+ style="overflow:visible">
+ <path
+ id="path4268"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path4262"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path4244"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-1"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-8"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-9"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-6"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mstart-7"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4265-8"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(0.6,0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-8"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-2"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-2"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-0"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mstart-5"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4265-7"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(0.6,0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1-5"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4268-4-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#30ff00"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1139"
+ id="namedview2987"
+ showgrid="false"
+ inkscape:zoom="0.8"
+ inkscape:cx="346.31962"
+ inkscape:cy="474.02351"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer4"
+ borderlayer="false"
+ fit-margin-top="-100.6"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ showborder="true"
+ inkscape:showpageshadow="false" />
+ <g
+ inkscape:groupmode="layer"
+ id="layer4"
+ inkscape:label="bg"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#d1d1d1;fill-opacity:1;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect13505-6"
+ width="98.575218"
+ height="70.808708"
+ x="328.8374"
+ y="317.09564" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="boxes"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect2996-1"
+ width="187.88171"
+ height="52.881706"
+ x="75.764778"
+ y="5.5253706" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7"
+ width="634.0592"
+ height="73.027374"
+ x="60.830574"
+ y="130.24477" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.02648067;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-4"
+ width="635.80048"
+ height="74.768661"
+ x="62.169655"
+ y="315.43158" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.85834479;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-0"
+ width="886.87543"
+ height="106.64049"
+ x="-48.78373"
+ y="540.24988" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.13159013;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6.26318017, 3.13159009;stroke-dashoffset:0;display:inline"
+ id="rect2996-1-5"
+ width="223.0157"
+ height="109.20289"
+ x="409.68008"
+ y="420.63235" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.90856051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:5.81712091, 2.90856046;stroke-dashoffset:0;display:inline"
+ id="rect2996-1-5-4"
+ width="191.98872"
+ height="109.42592"
+ x="644.63062"
+ y="419.66205" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.08755708;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.17511403, 2.08755702;stroke-dashoffset:0;display:inline"
+ id="rect2996-1-5-4-6"
+ width="154.05972"
+ height="70.246925"
+ x="678.59509"
+ y="214.87654" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="blue headers"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#749aba;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.85091281;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9"
+ width="16.994427"
+ height="73.79715"
+ x="59.561817"
+ y="129.601" />
+ <rect
+ style="fill:#749aba;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.83000004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-4"
+ width="17.015339"
+ height="72.050293"
+ x="384.61731"
+ y="130.22485" />
+ <rect
+ style="fill:#749aba;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.86642051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-8"
+ width="16.978918"
+ height="75.107468"
+ x="261.76944"
+ y="315.16946" />
+ <rect
+ style="fill:#749aba;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.36914372;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-82"
+ width="48.412117"
+ height="14.17484"
+ x="-42.956367"
+ y="549.14984" />
+ <rect
+ style="fill:#97ffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.83000004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-4-1"
+ width="17.015339"
+ height="72.050293"
+ x="241.39912"
+ y="131.17525" />
+ <rect
+ style="fill:#97ffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.36399999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-4-1-3"
+ width="16.981569"
+ height="74.882637"
+ x="568.40881"
+ y="315.33447" />
+ <rect
+ style="fill:#97ffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.95599997;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-4-1-3-7"
+ width="49.319912"
+ height="12.752681"
+ x="-43.016232"
+ y="595.7439" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer5"
+ inkscape:label="red headers"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.83000004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45"
+ width="17.015339"
+ height="72.050293"
+ x="501.49307"
+ y="130.29137" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.84049058;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-5"
+ width="17.004848"
+ height="72.923683"
+ x="678.04279"
+ y="130.29662" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.85091281;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-1"
+ width="16.994427"
+ height="73.79715"
+ x="681.8158"
+ y="316.14957" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.86126781;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7"
+ width="16.984072"
+ height="74.670677"
+ x="500.62485"
+ y="315.92252" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.82472873;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-11"
+ width="17.020611"
+ height="71.613625"
+ x="175.33748"
+ y="131.40486" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.86642051;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-52"
+ width="16.978918"
+ height="75.107468"
+ x="62.221222"
+ y="315.0412" />
+ <rect
+ style="fill:#ff7b6d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.39574718;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-76"
+ width="48.805244"
+ height="14.612387"
+ x="-42.996674"
+ y="572.61749" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer9"
+ inkscape:label="unused space"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#dddddd;fill-opacity:1;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect13505"
+ width="98.575218"
+ height="70.808708"
+ x="402.22061"
+ y="131.06841" />
+ <rect
+ style="fill:#dddddd;fill-opacity:1;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect13505-8"
+ width="96.700218"
+ height="70.808708"
+ x="77.587402"
+ y="131.47064" />
+ <rect
+ style="fill:#dddddd;fill-opacity:1;stroke-width:1.79999995;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
+ id="rect13505-5"
+ width="220.21585"
+ height="72.839958"
+ x="279.26709"
+ y="316.08002" />
+ <rect
+ style="fill:#dddddd;fill-opacity:1;stroke:#000000;stroke-width:1.12016988;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
+ id="rect13505-59"
+ width="51.879829"
+ height="15.10388"
+ x="445.6301"
+ y="550.76691" />
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:1.12016988;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
+ id="rect13505-59-3"
+ width="51.879829"
+ height="15.10388"
+ x="445.62964"
+ y="574.00262" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer8"
+ inkscape:label="pad headers"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <rect
+ style="fill:#fffec5;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7-3"
+ width="49.88493"
+ height="73.447571"
+ x="518.21405"
+ y="316.16635" />
+ <rect
+ style="fill:#fffec5;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.86126781;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7-3-2"
+ width="16.98407"
+ height="74.670677"
+ x="245.17551"
+ y="315.48059" />
+ <rect
+ style="fill:#fffec5;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.02099991;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7-3-4"
+ width="49.474121"
+ height="72.084908"
+ x="193.07074"
+ y="130.93698" />
+ <rect
+ style="fill:#fffec5;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="rect2996-1-7-9-45-7-3-6"
+ width="51.75993"
+ height="14.072571"
+ x="445.05756"
+ y="596.40125" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer6"
+ inkscape:label="arrows"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:9, 9;stroke-dashoffset:0;marker-mid:none;marker-end:url(#Arrow2Mend)"
+ d="m 262.87951,51.152779 c 0,0 148.12631,-3.276651 187.01718,76.272861"
+ id="path3973"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend)"
+ d="m 681.9161,128.72302 c -22.09709,-49.497478 -148.13393,-45.873109 -179.42835,0"
+ id="path3988"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="M 386.69903,129.58525 C 361.95029,80.971668 231.48641,62.20327 177.21864,130.46914"
+ id="path3990"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend)"
+ d="m 60.546017,172.89554 c 0,0 -32.703692,23.86486 -60.10407166,-3.53553"
+ id="path3992"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="m 176.82896,203.22242 c -47.24941,74.32926 -107.438064,49.90804 -116.0476,3.53553"
+ id="path4035"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="m 502.04581,203.43962 c -25.63262,33.58757 -82.31601,45.11485 -116.67261,2.65165"
+ id="path4037"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="M 763.23339,214.04621 C 748.83403,184.37018 738.54555,166.795 699.15183,161.8971"
+ id="path4039"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-mid:none;marker-end:url(#Arrow2Mend-1)"
+ d="m 769.42057,285.19885 c -0.88389,83.96892 -68.50098,75.57203 -68.50098,75.57203"
+ id="path4041"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="M 682.35804,313.04117 C 652.306,280.33749 539.16892,270.61477 501.16193,313.92506"
+ id="path4043"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:9, 9;stroke-dashoffset:0;marker-end:url(#Arrow2Mend)"
+ d="m 415.42523,202.55574 c 0,36.23922 -4.41941,88.38835 -35.35533,109.60155"
+ id="path4045"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:9, 9;stroke-dashoffset:0;marker-end:url(#Arrow2Mend)"
+ d="M 375.65048,315.69282 C 336.75961,232.60777 166.1701,311.27341 143.18912,205.20739"
+ id="path4047"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="M 263.39727,315.69282 C 245.7196,288.29244 86.62058,275.91807 62.755726,313.04117"
+ id="path4051"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend)"
+ d="m 61.790091,352.05822 c -25.819377,20.1091 -49.573204,20.1091 -61.96650422,1.43636"
+ id="path4053"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:7.65, 7.65;stroke-dashoffset:0;marker-end:url(#Arrow2Mend)"
+ d="m 448.12892,630.25126 48.61359,0"
+ id="path5241"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:2.09116507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend);display:inline"
+ d="m -39.741559,626.33548 c 10.599699,-0.12345 25.528414,-0.12564 43.719789,-0.81161"
+ id="path4053-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1)"
+ d="m 499.39416,389.93904 c -46.84583,17.67767 -206.82873,31.8198 -238.64854,1.76776"
+ id="path13236"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1);display:inline"
+ d="m 502.12201,419.58783 c 2.37436,-10.40132 1.73096,-5.65101 4.38262,-26.86421"
+ id="path4043-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend-1);display:inline"
+ d="m 517.94842,353.38466 c 19.7099,0 43.91577,-0.61421 66.57012,-0.61421"
+ id="path4043-4-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend-1);display:inline"
+ d="m 501.71494,363.4321 c 19.7099,0 157.04077,-0.61421 179.69512,-0.61421"
+ id="path4043-4-3-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend-1);display:inline"
+ d="M 728.67747,419.79091 C 702.92683,395.63959 592.90843,427.2649 577.43509,389.1767"
+ id="path4043-4-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend-1);display:inline"
+ d="m 60.975741,169.05711 c 19.709901,0 90.307569,-0.61421 112.961919,-0.61421"
+ id="path4043-4-3-9-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer7"
+ inkscape:label="text"
+ style="display:inline"
+ transform="translate(79.549515,-4.4031235)">
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="90.732231"
+ y="36.767765"
+ id="text10506"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508"
+ x="90.732231"
+ y="36.767765"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">struct malloc_heap</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="580.66718"
+ y="107.47876"
+ id="text10506-2"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1"
+ x="580.66718"
+ y="107.47876"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="438.12686"
+ y="223.50792"
+ id="text10506-2-5"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-7"
+ x="438.12686"
+ y="223.50792"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="581.31598"
+ y="298.638"
+ id="text10506-2-61"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-89"
+ x="581.31598"
+ y="298.638"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="274.6084"
+ y="99.764236"
+ id="text10506-2-2"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-79"
+ x="274.6084"
+ y="99.764236"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="301.12491"
+ y="423.26556"
+ id="text10506-2-54"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-3"
+ x="301.12491"
+ y="423.26556"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="133.18704"
+ y="303.94128"
+ id="text10506-2-1"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-2"
+ x="133.18704"
+ y="303.94128"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="16.340637"
+ y="561.27954"
+ id="text10506-2-3"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34"
+ x="16.340637"
+ y="561.27954"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Free element header(struct malloc_elem, state = FREE)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="16.996887"
+ y="583.24792"
+ id="text10506-2-3-1"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1"
+ x="16.996887"
+ y="583.24792"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Used element header(struct malloc_elem, state = BUSY)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="108.84206"
+ y="161.39597"
+ id="text10506-2-6-8"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-7"
+ x="108.84206"
+ y="161.39597"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">size</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="62.299515"
+ y="119.27286"
+ id="text10506-2-6-4"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-2"
+ x="62.299515"
+ y="119.27286"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Memseg 0</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="63.905106"
+ y="406.73242"
+ id="text10506-2-6-4-7"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-2-7"
+ x="63.905106"
+ y="406.73242"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Memseg 1</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="-25.028084"
+ y="192.57199"
+ id="text10506-2-9"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-31"
+ x="-25.028084"
+ y="192.57199"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="-26.795866"
+ y="379.95526"
+ id="text10506-2-98"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-6"
+ x="-26.795866"
+ y="379.95526"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="416.73682"
+ y="269.53305"
+ id="text10506-2-6-5"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-0"
+ x="416.73682"
+ y="269.53305"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">next_free</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="228.00418"
+ y="259.55359"
+ id="text10506-2-6-5-2"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-0-8"
+ x="228.00418"
+ y="259.55359"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">next_free</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="356.16727"
+ y="55.376503"
+ id="text10506-2-6-5-6"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-0-0"
+ x="356.16727"
+ y="55.376503"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">free_head</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="49.218113"
+ y="254.00189"
+ id="text10506-2-9-0"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-31-9"
+ x="49.218113"
+ y="254.00189"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">prev</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="690.51538"
+ y="236.82936"
+ id="text10506-2-6-0"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-06"
+ x="690.51538"
+ y="236.82936"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Dummy Elements:</tspan><tspan
+ sodipodi:role="line"
+ x="690.51538"
+ y="256.02936"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13581">Size = 0</tspan><tspan
+ sodipodi:role="line"
+ x="690.51538"
+ y="275.22937"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13583">State = BUSY</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="541.03906"
+ y="347.20566"
+ id="text10506-2-6-8-8"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-7-9"
+ x="541.03906"
+ y="347.20566"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">pad</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="16.661926"
+ y="605.21631"
+ id="text10506-2-3-1-4"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-4"
+ x="16.661926"
+ y="605.21631"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Pad element header(struct malloc_elem, state = PAD)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="17.290833"
+ y="627.77881"
+ id="text10506-2-3-1-6"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-0"
+ x="17.290833"
+ y="627.77881"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Generic element pointers</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="429.11118"
+ y="449.84528"
+ id="text10506-2-6-6"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ x="429.11118"
+ y="449.84528"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13711">Malloc element header:</tspan><tspan
+ sodipodi:role="line"
+ x="429.11118"
+ y="469.04529"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13713">state = BUSY</tspan><tspan
+ sodipodi:role="line"
+ x="429.11118"
+ y="488.24527"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13715">size = <size></tspan><tspan
+ sodipodi:role="line"
+ x="429.11118"
+ y="507.44528"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan13717">pad = <padsize></tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot13719"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
+ id="flowRegion13721"><rect
+ id="rect13723"
+ width="968.73627"
+ height="188.26718"
+ x="-81.317276"
+ y="460.64972" /></flowRegion><flowPara
+ id="flowPara13725"></flowPara></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="594.30859"
+ y="378.91797"
+ id="text10506-2-6-8-8-1"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-8-7-9-3"
+ x="594.30859"
+ y="378.91797"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">size</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="505.86865"
+ y="563.34613"
+ id="text10506-2-3-1-6-8"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-0-4"
+ x="505.86865"
+ y="563.34613"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Free / Unallocated data space</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="660.39099"
+ y="449.92532"
+ id="text10506-2-6-6-0"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ x="660.39099"
+ y="449.92532"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan14527">Pad element header:</tspan><tspan
+ sodipodi:role="line"
+ x="660.39099"
+ y="469.12534"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan14531">state = PAD</tspan><tspan
+ sodipodi:role="line"
+ x="660.39099"
+ y="488.32532"
+ style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas"
+ id="tspan14533">pad = padsize</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="506.5249"
+ y="584.28369"
+ id="text10506-2-3-1-6-8-7"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-0-4-2"
+ x="506.5249"
+ y="584.28369"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Used / allocated data space</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:120.00000477%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="506.18994"
+ y="605.30322"
+ id="text10506-2-3-1-6-8-7-0"
+ sodipodi:linespacing="120%"><tspan
+ sodipodi:role="line"
+ id="tspan10508-1-34-1-0-4-2-1"
+ x="506.18994"
+ y="605.30322"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:120.00000477%;writing-mode:lr-tb;text-anchor:start;font-family:Consolas;-inkscape-font-specification:Consolas">Padding / unavailable space</tspan></text>
+ </g>
+</svg>
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 036640c..176f2c2 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -43,7 +43,6 @@ Programmer's Guide
intro
overview
env_abstraction_layer
- malloc_lib
ring_lib
mempool_lib
mbuf_lib
diff --git a/doc/guides/prog_guide/malloc_lib.rst b/doc/guides/prog_guide/malloc_lib.rst
deleted file mode 100644
index 6418fab..0000000
--- a/doc/guides/prog_guide/malloc_lib.rst
+++ /dev/null
@@ -1,233 +0,0 @@
-.. BSD LICENSE
- Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Intel Corporation nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-.. _Malloc_Library:
-
-Malloc Library
-==============
-
-The librte_malloc library provides an API to allocate any-sized memory.
-
-The objective of this library is to provide malloc-like functions to allow allocation from hugepage memory
-and to facilitate application porting.
-The *DPDK API Reference* manual describes the available functions.
-
-Typically, these kinds of allocations should not be done in data plane processing
-because they are slower than pool-based allocation and make use of locks within the allocation
-and free paths.
-However, they can be used in configuration code.
-
-Refer to the rte_malloc() function description in the *DPDK API Reference* manual for more information.
-
-Cookies
--------
-
-When CONFIG_RTE_MALLOC_DEBUG is enabled, the allocated memory contains overwrite protection fields
-to help identify buffer overflows.
-
-Alignment and NUMA Constraints
-------------------------------
-
-The rte_malloc() takes an align argument that can be used to request a memory area
-that is aligned on a multiple of this value (which must be a power of two).
-
-On systems with NUMA support, a call to the rte_malloc() function will return memory
-that has been allocated on the NUMA socket of the core which made the call.
-A set of APIs is also provided, to allow memory to be explicitly allocated on a NUMA socket directly,
-or by allocated on the NUMA socket where another core is located,
-in the case where the memory is to be used by a logical core other than on the one doing the memory allocation.
-
-Use Cases
----------
-
-This library is needed by an application that requires malloc-like functions at initialization time,
-and does not require the physical address information for the individual memory blocks.
-
-For allocating/freeing data at runtime, in the fast-path of an application,
-the memory pool library should be used instead.
-
-If a block of memory with a known physical address is needed,
-e.g. for use by a hardware device, a memory zone should be used.
-
-Internal Implementation
------------------------
-
-Data Structures
-~~~~~~~~~~~~~~~
-
-There are two data structure types used internally in the malloc library:
-
-* struct malloc_heap - used to track free space on a per-socket basis
-
-* struct malloc_elem - the basic element of allocation and free-space tracking inside the library.
-
-Structure: malloc_heap
-^^^^^^^^^^^^^^^^^^^^^^
-
-The malloc_heap structure is used in the library to manage free space on a per-socket basis.
-Internally in the library, there is one heap structure per NUMA node,
-which allows us to allocate memory to a thread based on the NUMA node on which this thread runs.
-While this does not guarantee that the memory will be used on that NUMA node,
-it is no worse than a scheme where the memory is always allocated on a fixed or random node.
-
-The key fields of the heap structure and their function are described below (see also diagram above):
-
-* mz_count - field to count the number of memory zones which have been allocated for heap memory on this NUMA node.
- The sole use of this value is, in combination with the numa_socket value,
- to generate a suitable, unique name for each memory zone.
-
-* lock - the lock field is needed to synchronize access to the heap.
- Given that the free space in the heap is tracked using a linked list,
- we need a lock to prevent two threads manipulating the list at the same time.
-
-* free_head - this points to the first element in the list of free nodes for this malloc heap.
-
-.. note::
-
- The malloc_heap structure does not keep track of either the memzones allocated,
- since there is little point as they cannot be freed.
- Neither does it track the in-use blocks of memory,
- since these are never touched except when they are to be freed again -
- at which point the pointer to the block is an input to the free() function.
-
-.. _figure_malloc_heap:
-
-.. figure:: img/malloc_heap.*
-
- Example of a malloc heap and malloc elements within the malloc library
-
-
-Structure: malloc_elem
-^^^^^^^^^^^^^^^^^^^^^^
-The malloc_elem structure is used as a generic header structure for various blocks of memory in a memzone.
-It is used in three different ways - all shown in the diagram above:
-
-#. As a header on a block of free or allocated memory - normal case
-
-#. As a padding header inside a block of memory
-
-#. As an end-of-memzone marker
-
-The most important fields in the structure and how they are used are described below.
-
-.. note::
-
- If the usage of a particular field in one of the above three usages is not described,
- the field can be assumed to have an undefined value in that situation, for example,
- for padding headers only the "state" and "pad" fields have valid values.
-
-* heap - this pointer is a reference back to the heap structure from which this block was allocated.
- It is used for normal memory blocks when they are being freed,
- to add the newly-freed block to the heap's free-list.
-
-* prev - this pointer points to the header element/block in the memzone immediately behind the current one.
- When freeing a block, this pointer is used to reference the previous block to check if that block is also free.
- If so, then the two free blocks are merged to form a single larger block.
-
-* next_free - this pointer is used to chain the free-list of unallocated memory blocks together.
- Again, it is only used in normal memory blocks - on malloc() to find a suitable free block to allocate,
- and on free() to add the newly freed element to the free-list.
-
-* state - This field can have one of three values: "Free", "Busy" or "Pad".
- The former two, are to indicate the allocation state of a normal memory block,
- and the latter is to indicate that the element structure is a dummy structure at the end of the start-of-block padding
- (i.e. where the start of the data within a block is not at the start of the block itself, due to alignment constraints).
- In this case, the pad header is used to locate the actual malloc element header for the block.
- For the end-of-memzone structure, this is always a "busy" value, which ensures that no element,
- on being freed, searches beyond the end of the memzone for other blocks to merge with into a larger free area.
-
-* pad - this holds the length of the padding present at the start of the block.
- In the case of a normal block header, it is added to the address of the end of the header
- to give the address of the start of the data area i.e.
- the value passed back to the application on a malloc.
- Within a dummy header inside the padding, this same value is stored,
- and is subtracted from the address of the dummy header to yield the address of the actual block header.
-
-* size - the size of the data block, including the header itself.
- For end-of-memzone structures, this size is given as zero, though it is never actually checked.
- For normal blocks which are being freed,
- this size value is used in place of a "next" pointer to identify the location of the next block of memory
- (so that if it too is free, the two free blocks can be merged into one).
-
-Memory Allocation
-~~~~~~~~~~~~~~~~~
-
-When an application makes a call to a malloc-like function,
-the malloc function will first index the lcore_config structure for the calling thread,
-and determine the NUMA node idea of that thread.
-That is used to index the array of malloc_heap structures,
-and the heap_alloc () function is called with that heap as parameter,
-along with the requested size, type and alignment parameters.
-
-The heap_alloc() function will scan the free_list for the heap,
-and attempt to find a free block suitable for storing data of the requested size,
-with the requested alignment constraints.
-If no suitable block is found - for example, the first time malloc is called for a node,
-and the free-list is NULL - a new memzone is reserved and set up as heap elements.
-The setup involves placing a dummy structure at the end of the memzone
-to act as a sentinel to prevent accesses beyond the end
-(as the sentinel is marked as BUSY, the malloc library code will never attempt to reference it further),
-and a proper element header at the start of the memzone.
-This latter header identifies all space in the memzone, bar the sentinel value at the end,
-as a single free heap element, and it is then added to the free_list for the heap.
-
-Once the new memzone has been set up, the scan of the free-list for the heap is redone,
-and on this occasion should find the newly created,
-suitable element as the size of memory reserved in the memzone is set to be
-at least the size of the requested data block plus the alignment -
-subject to a minimum size specified in the DPDK compile-time configuration.
-
-When a suitable, free element has been identified, the pointer to be returned to the user is calculated,
-with the space to be provided to the user being at the end of the free block.
-The cache-line of memory immediately preceding this space is filled with a struct malloc_elem header:
-if the remaining space within the block is small e.g. <=128 bytes,
-then a pad header is used, and the remaining space is wasted.
-If, however, the remaining space is greater than this, then the single free element block is split into two,
-and a new, proper, malloc_elem header is put before the returned data space.
-[The advantage of allocating the memory from the end of the existing element is that
-in this case no adjustment of the free list needs to take place -
-the existing element on the free list just has its size pointer adjusted,
-and the following element has its "prev" pointer redirected to the newly created element].
-
-Freeing Memory
-~~~~~~~~~~~~~~
-
-To free an area of memory, the pointer to the start of the data area is passed to the free function.
-The size of the malloc_elem structure is subtracted from this pointer to get the element header for the block.
-If this header is of type "PAD" then the pad length is further subtracted from the pointer
-to get the proper element header for the entire block.
-
-From this element header, we get pointers to the heap from which the block came -- and to where it must be freed,
-as well as the pointer to the previous element, and, via the size field,
-we can calculate the pointer to the next element.
-These next and previous elements are then checked to see if they too are free,
-and if so, they are merged with the current elements.
-This means that we can never have two free memory blocks adjacent to one another,
-they are always merged into a single block.
diff --git a/doc/guides/prog_guide/overview.rst b/doc/guides/prog_guide/overview.rst
index cef6ca7..5d378e5 100644
--- a/doc/guides/prog_guide/overview.rst
+++ b/doc/guides/prog_guide/overview.rst
@@ -112,6 +112,8 @@ The services provided by the EAL are:
* Alarm operations
+* Memory managenent (malloc)
+
The EAL is fully described in :ref:`Environment Abstraction Layer <Environment_Abstraction_Layer>`.
Core Components
@@ -127,15 +129,6 @@ for high-performance packet processing applications.
Core Components Architecture
-Memory Manager (librte_malloc)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The librte_malloc library provides an API to allocate memory from the memzones created from the hugepages instead of the heap.
-This helps when allocating large numbers of items that may become susceptible to TLB misses
-when using typical 4k heap pages in the Linux user space environment.
-
-This memory allocator is fully described in :ref:`Malloc Library <Malloc_Library>`.
-
Ring Manager (librte_ring)
~~~~~~~~~~~~~~~~~~~~~~~~~~
--
1.9.3
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v11] doc: update malloc documentation
2015-07-16 7:37 ` [dpdk-dev] [PATCH v11] " Sergio Gonzalez Monroy
@ 2015-07-16 10:07 ` Thomas Monjalon
2015-07-16 10:12 ` Thomas Monjalon
1 sibling, 0 replies; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-16 10:07 UTC (permalink / raw)
To: Sergio Gonzalez Monroy; +Cc: dev
2015-07-16 08:37, Sergio Gonzalez Monroy:
> new file mode 100755
> index 0000000..d6bcc84
> --- /dev/null
> +++ b/doc/guides/prog_guide/img/malloc_heap.svg
This file has the exec bit.
Will be fixed before applying.
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v11] doc: update malloc documentation
2015-07-16 7:37 ` [dpdk-dev] [PATCH v11] " Sergio Gonzalez Monroy
2015-07-16 10:07 ` Thomas Monjalon
@ 2015-07-16 10:12 ` Thomas Monjalon
2015-07-16 10:39 ` Gonzalez Monroy, Sergio
1 sibling, 1 reply; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-16 10:12 UTC (permalink / raw)
To: Sergio Gonzalez Monroy; +Cc: dev
2015-07-16 08:37, Sergio Gonzalez Monroy:
> Update malloc documentation to reflect new implementation details.
Should you reword the memzone chapter and move it after the malloc one?
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v11] doc: update malloc documentation
2015-07-16 10:12 ` Thomas Monjalon
@ 2015-07-16 10:39 ` Gonzalez Monroy, Sergio
0 siblings, 0 replies; 108+ messages in thread
From: Gonzalez Monroy, Sergio @ 2015-07-16 10:39 UTC (permalink / raw)
To: Thomas Monjalon; +Cc: dev
On 16/07/2015 11:12, Thomas Monjalon wrote:
> 2015-07-16 08:37, Sergio Gonzalez Monroy:
>> Update malloc documentation to reflect new implementation details.
> Should you reword the memzone chapter and move it after the malloc one?
I didn't think that the memzone doc needed to change its wording for
this change.
memzone doc in general could be improve and extended but I'd prefer to
do that as
a post-patch if it needs to be.
Sergio
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v10 0/9] Dynamic memzones
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
` (8 preceding siblings ...)
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
@ 2015-07-16 12:05 ` Thomas Monjalon
9 siblings, 0 replies; 108+ messages in thread
From: Thomas Monjalon @ 2015-07-16 12:05 UTC (permalink / raw)
To: Sergio Gonzalez Monroy; +Cc: dev
2015-07-15 17:32, Sergio Gonzalez Monroy:
> Current implemetation allows reserving/creating memzones but not the opposite
> (unreserve/free). This affects mempools and other memzone based objects.
>
> From my point of view, implementing free functionality for memzones would look
> like malloc over memsegs.
> Thus, this approach moves malloc inside eal (which in turn removes a circular
> dependency), where malloc heaps are composed of memsegs.
> We keep both malloc and memzone APIs as they are, but memzones allocate its
> memory by calling malloc_heap_alloc.
> Some extra functionality is required in malloc to allow for boundary constrained
> memory requests.
> In summary, currently malloc is based on memzones, and with this approach
> memzones are based on malloc.
Applied, thanks for the big rework.
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v8 0/9] Dynamic memzones
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
` (9 preceding siblings ...)
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
@ 2015-10-14 0:12 ` Stephen Hemminger
2015-10-14 11:05 ` Gonzalez Monroy, Sergio
10 siblings, 1 reply; 108+ messages in thread
From: Stephen Hemminger @ 2015-10-14 0:12 UTC (permalink / raw)
To: Sergio Gonzalez Monroy, Thomas Monjalon; +Cc: dev
On Tue, 14 Jul 2015 09:57:04 +0100
Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> wrote:
> Current implemetation allows reserving/creating memzones but not the opposite
> (unreserve/free). This affects mempools and other memzone based objects.
>
> From my point of view, implementing free functionality for memzones would look
> like malloc over memsegs.
> Thus, this approach moves malloc inside eal (which in turn removes a circular
> dependency), where malloc heaps are composed of memsegs.
> We keep both malloc and memzone APIs as they are, but memzones allocate its
> memory by calling malloc_heap_alloc.
> Some extra functionality is required in malloc to allow for boundary constrained
> memory requests.
> In summary, currently malloc is based on memzones, and with this approach
> memzones are based on malloc.
>
> v8:
> - Rebase against current HEAD to factor for changes made by new Tile-Gx arch
Following rules in kernel. You need to fix the 32 bit build and resubmit whole
series.
Thomas, this patchset should be marked "Changes requested" in patchwork.
^ permalink raw reply [flat|nested] 108+ messages in thread
* Re: [dpdk-dev] [PATCH v8 0/9] Dynamic memzones
2015-10-14 0:12 ` [dpdk-dev] [PATCH v8 " Stephen Hemminger
@ 2015-10-14 11:05 ` Gonzalez Monroy, Sergio
0 siblings, 0 replies; 108+ messages in thread
From: Gonzalez Monroy, Sergio @ 2015-10-14 11:05 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
On 14/10/2015 01:12, Stephen Hemminger wrote:
> On Tue, 14 Jul 2015 09:57:04 +0100
> Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com> wrote:
>
>> Current implemetation allows reserving/creating memzones but not the opposite
>> (unreserve/free). This affects mempools and other memzone based objects.
>>
>> From my point of view, implementing free functionality for memzones would look
>> like malloc over memsegs.
>> Thus, this approach moves malloc inside eal (which in turn removes a circular
>> dependency), where malloc heaps are composed of memsegs.
>> We keep both malloc and memzone APIs as they are, but memzones allocate its
>> memory by calling malloc_heap_alloc.
>> Some extra functionality is required in malloc to allow for boundary constrained
>> memory requests.
>> In summary, currently malloc is based on memzones, and with this approach
>> memzones are based on malloc.
>>
>> v8:
>> - Rebase against current HEAD to factor for changes made by new Tile-Gx arch
> Following rules in kernel. You need to fix the 32 bit build and resubmit whole
> series.
>
> Thomas, this patchset should be marked "Changes requested" in patchwork.
The v10 patchset was applied in 2.1.
Could you elaborate on the issue?
I can build without errors for target i686-native-linuxapp-gcc
Sergio
^ permalink raw reply [flat|nested] 108+ messages in thread
end of thread, other threads:[~2015-10-14 11:05 UTC | newest]
Thread overview: 108+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-08 16:37 [dpdk-dev] [RFC PATCH 0/2] dynamic memzones Sergio Gonzalez Monroy
2015-05-08 16:37 ` [dpdk-dev] [RFC PATCH 1/2] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-05-08 16:37 ` [dpdk-dev] [RFC PATCH 2/2] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-05-12 16:30 ` [dpdk-dev] [RFC PATCH 0/2] dynamic memzones Olivier MATZ
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 0/7] dynamic memzone Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 1/7] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 2/7] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 3/7] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 4/7] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 5/7] eal: remove setup of free_memseg in ivshmem Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 6/7] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-06-06 10:32 ` [dpdk-dev] [PATCH v2 7/7] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 0/9] Dynamic memzone Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-06-19 17:21 ` [dpdk-dev] [PATCH v3 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 0/9] Dynamic memzone Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-06-25 14:05 ` [dpdk-dev] [PATCH v4 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 7/9] app/test: update unit test with rte_memzone_free Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-06-26 11:32 ` [dpdk-dev] [PATCH v5 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-02 12:14 ` Thomas Monjalon
2015-07-03 8:16 ` Gonzalez Monroy, Sergio
2015-07-03 9:02 ` Thomas Monjalon
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-06-26 15:29 ` [dpdk-dev] [PATCH v6 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-06-26 16:13 ` [dpdk-dev] [PATCH v6 0/9] Dynamic memzones Ananyev, Konstantin
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 " Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-07-03 9:55 ` [dpdk-dev] [PATCH v7 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-07-13 13:15 ` [dpdk-dev] [PATCH v7 0/9] Dynamic memzones Thomas Monjalon
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 " Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-07-14 20:41 ` Thomas Monjalon
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-07-14 8:57 ` [dpdk-dev] [PATCH v8 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-07-15 8:26 ` [dpdk-dev] [PATCH v9 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-07-15 14:07 ` Thomas Monjalon
2015-07-15 14:11 ` Gonzalez Monroy, Sergio
2015-07-15 14:48 ` Thomas Monjalon
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 1/9] eal: move librte_malloc to eal/common Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 2/9] eal: memzone allocated by malloc Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 3/9] app/test: update malloc/memzone unit tests Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 4/9] config: remove CONFIG_RTE_MALLOC_MEMZONE_SIZE Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 5/9] eal: remove free_memseg and references to it Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 6/9] eal: new rte_memzone_free Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 7/9] app/test: rte_memzone_free unit test Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 8/9] doc: announce ABI change of librte_malloc Sergio Gonzalez Monroy
2015-07-15 16:32 ` [dpdk-dev] [PATCH v10 9/9] doc: update malloc documentation Sergio Gonzalez Monroy
2015-07-16 7:37 ` [dpdk-dev] [PATCH v11] " Sergio Gonzalez Monroy
2015-07-16 10:07 ` Thomas Monjalon
2015-07-16 10:12 ` Thomas Monjalon
2015-07-16 10:39 ` Gonzalez Monroy, Sergio
2015-07-16 12:05 ` [dpdk-dev] [PATCH v10 0/9] Dynamic memzones Thomas Monjalon
2015-10-14 0:12 ` [dpdk-dev] [PATCH v8 " Stephen Hemminger
2015-10-14 11:05 ` Gonzalez Monroy, Sergio
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).