* [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 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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 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 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 literal 81329 zcmY(q1z1$y8#Rg|AV`RGgCm^|NQcybbcfR2jWkF~4Bai#Aqa?sbazQA-Jx{n-Q(~7 z-TU3=QOB7%bIv~dt+m$so-ic^DJ%?93?w8ZtT)mUDo9Ak#7IaFRUe~*Pa+b(`hk}R z&MH!3Nae%i+u+S3b5VIwB&5pdCpU&D;61v7w6-%65_Tuz?}J2UY%(OI`}{W&qH6AX ze=^Yw)ViiV;Ka!6%YS{yWVHVwT=z9D+51Y=UF09lR08j_1OB3)mCt-d`YbFkBk=i| zgy%kOWYF!VfET@u*fVZf>e>0@ZCbC*Ietzzv*E#1&Kfc7mzW(K|Nay`v_kX4`rjK4 zZc^3%J_i35LFL;1&zszS4wV0M44kP0AI`wYzz}0yUJtXHEK%RkC{&=BH;T>A%j*n= z+o}#&O_w+IncSSV+-^jFfx{Oo`@L^qA<0RJ3*5EvO_9?H)xZpU;t)gP5M{o>2(<+7 z+tcZ)`@3t|AMgL>8TUl}dL39}J-xFCizI#H>Dj=d_4V~Rq*!_??+sq!P&yC$V{NIQ z1@h|LZhLdoCKSEn<Kz8;724HiacOC9*P*0YrcKBr0XEC+!J@tV?jodBvCt@1CMFe< z!#ejvBdkrHs#n%i78!?LeQ~s1&;=fLc16>#-j`0d5iAghxRaC9RJ>zkgX?a8e<E|f zXdo(SxBY947GEEsB;ya(NF#%XvdL_TgM)Hd14BcTEuG4%-I1r6b?gT^PZ?J|E*ssc z46^H<`|?FKiVGrb&oz2DrwVy}7be=gZ0r&W^)ty|o{bqwd&T@%8?B0v^=&Vlgfk$< zm3q~e(|j0Hto@)&-}f?~++|esXuB{QH3zXxhQwpW7_bDn3|?ihF1pPuuX4eIR%Fp4 z!xw}MuQK^v<21g!?YlT!4OIGPCivD%ykdr&<P5#PH#axbogB|_M8rB7!vCEL#*z|% zOPf+PdeIJ>=XgeNbiv+oqhd2<`kzI#<=R5y+S|o!U0%i+5{Jn&d7kFE?Jw}ZHUIZL z?vtsvQP1!(3rc4a2q3YBk5+%|H#27$*!^F>{%-rbKT2S)C_KdQ1=I|~0`XOMw*k0= zCDfDJMPJqP&mplz2BbB&XTQ?}{#(=_Vo@^%)ocN&(j;M+!<ZW*+4giQ|L(pM&Pc2r z@LR#t?aQ+13+S37;cG6Qn*Y8E2VaSe*olhzr$!N@V;!^m-d&nIt6C$j3Hx_axji#Z zo*LM5)ml4UzN@X=A^9)=F8R{<%Q{1j)bx*%S8$2o#2vqzLn(?%&VOMMilWN#`4LY| zrhr?NK2MEm$eY@?$R;*le~hV}p32~P+{(LF$lqc+KE`mdvn!9BAVDk!mvbsS^x52m zLI&^H+yp5))}_~vzu&9tVgKJv#HFSdpKTwuCK%3t^Y5Lqoxvx9YM{E1zrZsII{Dm_ zm33{0Z1S2X3jGY?bu=)CqhF!%#SUsCQ@o4vG1w8>-i^(0h`B4G`Z~;dI_|+3CVc{L z;l%B<*A#W*Lm1XUPja)!{)#EVJpEDQ!8J~V`{Dy*^oTv}5;D15LN*<Xh4KIHjs)CY z(3^^ry(#zKF4G5pMT#Qe=)o@%gw#w%o67rstA_ko)TJE=#&PqCLWfA8{xH7rtq)2J zV>9NRvNdk#{d5<<zB4|}?5`>aJH?`^7#UUOS0E*?9N}!kCz<k_uc`j{J`q<uq<%D< z;Zkr`oL;8F?k=M$%T;{Z`EYcaDx1L3@7!kIv#&E})?e=VUUkkUB~9S-1vjG#<vqex zQ-hDMbpmO!d90@f={(5(+uH&I33Q<b2BH(`X$yXrIJR<sfp9|akN(hR9hB#?R~lv7 zL8(w}y7v@Jn%Bl&*vX?8{wj{|>Ps<C>9>v(7P1-QYhEpgqDJBLIk4}{D>f*kyqH~{ zuVOqEq(H?^Dno~ua!p5UYeQCYj5l4+v!QQc3cYb{ddyq1Zu36e7jv=u0!panDBHMb zO9f{iseF}^2mjfXxDw&m9QEli&7tg@U#cNL@fMjm7Q^~CiXK+c_XRC&78sTBD63SL zVtrjWc1al44a`=*D@*x+GNMsRWK2O@G~wKXp7MawdGICo{~ZhrM2@QkNm*Wl&SJ(d ztUHTZ$r)i=lQ_N_LWA-}wBr0|X3|#gYaUE_W!a>zy*vMR&fuX>?dd$uC4<h2v3+=f z*^WqdR@xRLz#IQ<A~V>;KEii4xI}hVuFx8)npP8rn^Gg06xRBU<0f38qh319IIpIp z&++N9ihrVTeVSd(q-powQRZiv_kX2<ezq3b9-=Kd(iS*L^3|2<eJa8!aP*eUdk-C2 zbn&30Ktnk-cM?e{MWaIR3xxCk+Mdq9^k1eP19_JpKALQG>KXpb%tU)r=(dkN(&m7* zVT?^q(*zx1v~#F<-{XvJrmo3(?rH9(V8%)^=?1qJ_PV^|7xjKlr>W5W>Ia;JO=zG_ zFCTq_-ktM>-(onrqSo}L<ED#=nAf3TL>yEu`9xb?Lp#O%C9DB+hgiot?_>bGyryl_ z?CLru^!Td3i1+Y$Vp0;_Z=34KiDFgy$NyEsBJvtB7j06qwP}67#B$vq{d}7l5FDWs zrKfMJFFY1ToG$AI*1yCrj^D282`Sv$McsCjM4s0AUrs#T&Y+;u%ay4%>cW_{t)0)4 zPv?gE-ds>m{41akPee*x^t;z>aNB1q%zU3Fc+?9KwHWGPCOK%0#o1aMs?>)hVh*PD zr+=7tEi$nm?NDRstPSw@nD9rVWH{ZOP971yW_kSTzv!hz<R%p3``hzm&Jn?C7&GyQ zxCcLK7Ub{k_Ix0|+EpfD<$<Dl*&K#}oJRkC!ZC%Y*_Z3d`rpP}7d$p6weT1_X0)`> zR`cQ&;?@U}2R5_)D1-hviJ?Ta{87|1QoXtZE}ubZ6NjFg-}Wt&?)H}d>^Y%>xS98V zfAuK{{58R<aR1DtvlB(}^b9rs9YOu(m$t7)LdP6dcL8z#9j;bJfJ<9#J~HJ&{AyPI zi*nbePbX;%;?;q{XWgMEJKS<Bt-oysmkQpOe)+Bv-2dN(5hAW}a^f=ojH;0uE2a`x z=zKmz-qH529qkznwCr1%n2SkUHeRK$MA$*-Sim?0H}?8wkNY*M%kk!9+yQ^!p3p=f z{;ql_?@^4<8#)s#y-lnVbN#+FjyF|-^AbS>jmd1fC6Iqv+wgw@?9`S-j>SIBKsCuT zXPrHS;Z2dhn%9PVeQp)l)X{pl%rUQJV>iDuwT@?Z;YNa&!BY@{xieDqy6@mm4qh%5 zYYK;$$=4RWBsg)q$Fk(P){&iD(S(r~?O>J+Gac>;Ia|&$u6Yy%Eu`UnQdFDW^8ibH zy*w2am4K)JmF#{)kVZafy@=NJ)FknA9sRmD`lg|*Rz#UD+2T?$)^J|ynkHPmx==1D zO!s4WO9uCfcJya8_mBR8wASs?Bov7uv9vz7r~L1e%E?Ggm$JIT#~N5wlC`J`kxofz zv;%p%ev!OF%OLSSpLeS=8%hJH0O1?CPKd(GK+M%(bF_N}rx;bH!-{e;<Qt3%$fng| zZ_IVsBfTlJn|M*0+?#^MNOPzN0KwW;ZuEED|6I$c`|W^oTzWb)A0J;^*0LdGcg*nY z7*8XK;bY$<gQf$U)NjR89uZR#7`)$CrMHtaFZxlI3JA?nH&}-dD}ZagM1jaSsQ%8E zCE}zA>wMLmn*LI_Bqst%t_+cle2f<qXV?gYO=q5xjMZeHY>ej*R&_57to@Coev8ML zCEp}1$;Tg$^c1I5<4aX6|G(V?i^5fgnh%qwa#_ZuM&WZxhG3C^{H56!`vLm#!E<!Q z=BycJUOoEvoNl=$?meQqCYa8g0nX)9^&5t>Ma^;y(E->^`TEl{P15otLgS23yzqUp z1)~#b0{WPi-?jD?`&R#hL;p(}Ab^qpoKa+}TYBiRmGdz`sY9AYqg;m(K!EgbcJpt$ zqp7I=-w2CD6tf>JVrIL@Hb}2L!v7pb&ph*vZIEdFU#hOGM3{xEamJ?A9NC4KVKtBs zYuC1mP7x;G_5VA>ebTU0wNT-y5pfurot4-5p6%t_zimxHZ0n%>msm^=r$ewh8D(W< za1<gcmH9w?Jp&-=1#(JNYWmxMTgHuQEh&L+XLWzEij#h0^@1w}W?R3W1TZye(7#_9 zs$nA%Pgj7RZX*o|>~p)!LfJXgenHR!WFpg@iDJ^b4jhqQG%9aueV^0B!E6!zj=Q^S z`}zB`-}a`nRi-<JSjQz5Eq8C5FE_|sJ)T4mvW^eI?h$ST;rr^!@!4dhGpqp{c7J!f z9xFwOE`|lEG{}o`<p9v7=-TJ{EYa`&dNFNZnUeICRi1TQhWAllTw)?^JiX%7Sk!8i zfC5|n+B3fUn`6`d1jdq>MepOGs@?J*{ed{bMcB`@TBCO>ySwWgS6#ln5E9aj;@@HD z8yrj+%atk((}<^&Ukimq#h_x5Zq>~6xR(FJh==|Ncv)e0zBj*~psElnMdicw_!Yd< zXsS$m9Fz`MD<rOyipC=ub#k-ejABC&8!D!s^;>+aJWs8#1a1yG){h3*HB&cNBG{Au ze*djdUcZsH@@AERp5ckxyle6vveO1nPUq|S2G{lcWWDj&IQndFmABt*7;S%j8#C8x zbg%S2n>FW8tLwlP=uI`xDjp^xrm<XTtk3!n;hq{Hz(>GXjkTujqgj~+*yp(H?22$? z2wor>&Qyp<?o%q?iyQ=liWYq^Ygw52Ek`kss=CeRd|s36V!!#ioCz;Q_OR)j)uc=} zoCG49#4_frJRX~yr<l$yAAQ|&e=U-j{r;C_*AwmLvtQPpBe)RQmhh*9n~T+GsMDJC zytxUo<fD9xi-YBoAdYsY>%X-FLFjlV%?$KClwM`z4y_NY?z&Dnq0kfW|NIOKf#kT? zQ>1l0xAO}Oh|!-M)4gBxaho-YU{&*<QFF*9VAik*r25IY;c#=cThSE(-K*@OWV#1* zXrh#WP6K$@S0=1zp|g~m(`o(j@3!wePA4_h(M8*k2Fn+GuWer~w$DXOo+}Gx`Q3TU z`(CaSsdIRq{b8Ke^ZF9i@9-C}F6rB=Q`@LSgpt)AAW+et+3Ff#X%;N<4rvLC31iQ1 zz+C;w`o1JVQ{-Bf-rM8(R)oPvADr5yABQ7jploQuHRhZKSozZeil>JK_E~D@->2*N zoKEsT3{VNIa+)<w?vv-4e=(5EKJD@1dcURRJ>?Au1Wz9wRc9Z39GFO5_CFUR>qeC3 zy&wB(^~4+o9|VEV{Nr_l#amNf(iOhm0oMr_y+DpuXCO*3&LQRqy8&mLR!+UY-S^|K zeH0wv@qBi6mOs30?LB^!u+P=j3)kVMm2l<>mN<LLF6(67_pwnF0y|si=NxX$i14oz zMp&SLbS4#tluLTAelrUIm1j6~W}I#T9++X+xs=)7M`2NKGdy>S`8_fe9>G`~CcTz} ziFS&5Tm6wE=KrXma`fGX(Pt?gGjA0CshQT}zyD`jOpqWp&KLbOQH=MS&NW6}9om)3 zS0$ff3kxLBGw(Ms$8~X5Haj<V69`=@3Y{i2MBkqLR16$l3#=zek$k^5r%hkA=(gx{ zG$L|eB<9qC#nU@eX*}6Mz#(?Dcz>t)eZkZE^5%GC5{n%|SGV>mA8}P_7$Td=<$e8I zKoqa1Kx&kKxbe)Cl>r6`Dq7H~3eQ3zdB3&bYVjf^#9^q)MDA1Ykx9m86OyD#EW9qL z%A^^0xISN$kfb@sc((rs+KAGW0B%&Hpti^52Zx3Fvbx0>_(kD)BUeU;Mi%_|07*Sf z%amc9<&$|?VAz)XczeW@*M3u>4C=yq3Q>DTG?AXWsGj#>*OG;@X<*+LV3i*yN;MUw z__f$O2TKqIPof7{-0+AwB5;f&k48%)7jvIW!dx5b85Em1`Fd^+ijT*zFG#v#`sxE8 zj<$B>PPhq|5%{XLLK-HROhEZ$T{ptv`w>1$Rz;b6i8QPu6ZQgYWZVJ!o%%psw*HUj zaayK>FAZ#;<Qka`rsPmoI{Y38#lh$X`oN^np5NUu72npU!0>5|wv3%2o<%jj2*MR? z;t`iYHv|xdOSI!7UYH*4t1?i)wLY0N-W&}y*rDNVM2p<H>xRbgQ8Lg1gqvs_#RuDT zdHJ@pF`2?`ZjA5=7x>)@K&Rk^n}+3i?_&lG6i6Tgv3r%#+Sb<&?4NwPWhx6_4aL>v zv>0N#9%yG~C@ascT`5Z;>==sHj(Pz6vm53=U7T*;tUcWq#$_sAY4DI2;}K=C)&b6R z-QbA(dZMP9hXH|$fzjp7<?$pd+nz(}zGE8-(U#Tv2}<SGCqz_DxY#@b=gh@0c*uj- zrc6hiZ{REWjU&y!up!&;PMheYvn(83UoEB{TBb3)g&IE)%efi&iVv(DGndW?<KHq` zdr>0JDnIj5=4!>*8@D;nro1#ZnJ7{&(aLaJ@Nfcox%A;fpnm*-QwqKHy*<@*RcgH@ zVf?p=X|FTWNq+Kx^BD#3J^NXxIKAkeX1w-LwV*qa%vy_ODc=TU%ISJyxYpP2sFW=! zkB23kG$_nX0x!uxiENNd<9Z|iCFR9GS!FUZ(g56)&7;BE8MAa-7hO!!JCK>`8*)%I zkVJNha!=gU+Za%f`8I|#OU<;O5V1`-%q>9s{WqBsvi@ZRCwyGQ7SCj)AQ)yPE=Te8 zq^5qNqVT0eVkDh>T5m9sUJ>N!U|-!{!>$^m0e--jl2*v1eakH!02k`VEHDK<&uv;= z^$e8fFH=2N!syk;THz5j@`w#Hb~gCmhIxmk+cmtoI{V8k{t;W<bhWgiS%n144@QEt zj!cV$f^=S;9>AztP=r=up5*}}O7gziE_5yL?xFH4a~8L&TSncU`tr`<8^VG%r2pi{ zP+X0DVIUZ;1G6dT`|~{oJ735|)_Q~PzWb&2WWNoKiUpL+Dol{c&UsMaHi%N^$0?d| zeV9?k-*Ud-wVM&i=e$w0jZaz87@>pMg(4=KxLTkN!8xV_Jc!_OjX-+j_w?6(W<b~# z`oK)3%ytZ5QQJYk;~}1s4^N{!2sRD*)zz_X`CWIm$g#K+C^rlb$TaKF$Q?eEKOeqz z^clcd`LWkf`pxnu9q;dd?*CsD80CR|r~+kYdmoV&0R1Z``^0;5d%k!y@3y#29y-AG zLr(JmMj|?Ez(pe>qM!JL@aHOfzl{3NPol_tK;oO6{EI6^p2HlGd&8pFs3oPxk!eJI z4SqZsi<M=pn(BbyFn#_&GXq=pP^)7F6wKso->c%fwy5G~SQqc?>Lr`+uB;Q6hm#H> z*czm#$7Y_wy=~x6bY34C5*#A9AE>Krx7X*uqb?=0k5i1JVg>f<#1rWedxa3f5aEoA z`P2_Mywj*CTJa^mowXxvL}9V20-vN5pM&v$C(+<~E4=u53PThhUijm18TD2Z2fAR9 z%e8o@Vf&*~;Vuizew<u#O?@BN#Q6bmMrrJS=A8)|b?-^XFOX&+xG|f@tdQe~a{&Cr z8bYaAOW>beVxH}zL3Xc>wV@(%a}a1#GpWI^xRz#Ju8ddib2cjlC@9yfLnv4v+~S`? zZOqxh@Mxicc&K%)FI~vC_XIt;&KKWAH2|F;mwj+RtI4y5i5UL1@nR*y>9C7v_XhV} z>j+p51I~Z9$vN0!;DZT!bs!O}Nk7$7^mxN^X(Q>)irlyvmx*e5aoc0r_wT>|gB<|< zkqEkf1?nwKNQJ^<Eq3RDwee;Ea9!fCFh<17<fHJ1601V}>@z)Sq;PkRG{?U+(>o>0 z>BxiClpbrislq-DCT3<OQf0H%=CZl4NE3i*l)4bx&e8fW2a{sk*`?OD38ITEr;TAY ziWstWHDYggB4P9tq$NadPkc4(aQO=dM7hb&%BtjKcUrOh#~%OoT)neH9tfh?!3pd; z1gW1y%E}>*EqYAnziQjhjLkrvqJ6qGCN5v7B}$%LZF6%5fv69?|7}Z;BYg1*+@%jT zkUcUV-+@v)Fu>lz!f7@bv*>%x%KHmpI*tEwE2$wiDqd0QyS7Jm8>SKg58Jm;3Y5u_ z5wO<N^EoC7oyH9+oq4s>MaYJW8VV4%6M(Ry3xw+YTp|V%1Zc0};#h=MGXm?ab^dq1 zND$nPk2spn^!QkGj>x%WDI!>iz;0?6HO_i7l_K-<O~x_bM?T!LrkC&kXbDAk-GRjU z_;FS#_hjcE&;fW;q1WW{2pLCJ@CgK=8^VqZ|7pmtkOniANN2Rv$?#qfu87kd^rINu z1r_7>a9;V}0RK;_Xugt3e3FoWz{Sy$m9NJP42AIt5@L^*-j)edGQYE_3~mJQx8#q* zBH+&3X%vD__B#W75tnc>fFP{>>M4wArk~W2Is+z{OMw7<Ot>j;Ua}j5JT>L4L`*zM zO>X+A>(Pj6bMK}Ow_KHy)`3dbnyp+JH|k(MV2Z_dcFNquV@uj2ysMZVOa5qizPDFx zv4~52{#TJ;<Q+0gDWW-ApCmZWALc*k>Zl-(suG1Pj!d?$v*7jI@{_|kJ{fR~D<@JG z>G_<h17zoAGM@K_zoEV9=lFP@?vMB5CF-S148X-8WB&pM@}RXE{}VOGxzSvmV+rqv z>{|aL|K|b46N)0Yc`bZrpCj1D$58#e)dMW2{?;y*<4urQdH~3{79);57W+)ZavtFk zEdPR8nuEAz3%GKnCeu`TtGCtOT)opU-)1jBy%oC}A9ymwmP5_jS|XPLl&hTsph-nb ztH@dIAZYTFXy9uEpMHVC-3Rg~q|Lx&C0<c@Yz%qB@RI{3F~@6D2K-nETCWldca75n z@es^du)+G-(U+8~J<*!9Tfj*%qY!cP>;V~~YbEVK+;rf~q2;{#DG+;{phfvLaZ}xP zLAs0N!B7{D$PG2?+iyzInr?HBA8i0ZZ?HohX(dOr!63~q5uGkbn873DV@A2clv@9| zLj)RqSXEELX$IGEomA(qCs!@j_q$d9_1)H524SBJ)y<8_(>I7lM<<rtLy^NyJXfyV zq9SSFGVHoR75p=HKmML9037tpGo9OZ4S?S1H`{{xR#sO30u@(r<}o-=i1L>*?P}Qc z=EZc&9W6jxbxeeEuoggJCz=pF3tat~7xrLvxo<^tT3IkqRzDzP$K2d_djPO)(iVWy zhKpE?py5A%ghd*H4j5o4wMEIrthIA0&DVPZ+dm7V|5OdI{dc5%4<hO?bGt<+J55{& zG5`FkDV_7&{o(}l3se=vH+07T!Uq-^=s{wh;TZDGdRl=_E`>T-&(o3`0d%bR|I@4| zq>Hi~Ouc^F*1l=+y#+}LaJXLDWSwW^h+;k=@n0{|iYtPMJt>h{b4L)9?nADD0aP6v zz4Z9sr3`lg$Wdks4i4^Olh_<gg}Cla#DYDyxERluty%6#Ljf{k5@-wpa<A&*cX_nV zl>4&R1ALHV`Lp27m$x-0prKb~HOX{J-uY^0vb1n|mAHc;0(8u*?yfe{?YZuKfCM@Y z@aOhIlb1t~VJE6}o6^~ZQ+O2%J-rkVty!HnhbOBn0l9yRt&qvDKEPwbqJoY{T?q;J zr|#V~y@d&0+uPe+)7)^AP8jeL^iM}7U1q{=O!A(-mRr^P5g!5q9)96<c6Oe`L^1t9 zkX5Xt%p!bqApTc$V;|8I!+11pvdoSD#6;xoBA3hdms%oP!s9=i0D{;9@RJUw645)G zb5=Zk7UZ4nbH<qD7@frW&Px25HV`PjjhhV&4o+;YdI+j5{QX^<e0jV%0BT^~rh1Re zME6FpcV90`cy}LtcG+bN`jT)oF{%teUZw4-bYI6}Mfs#w7nyMq+!7+z82eKzjeBG^ zhcnlIDvBfmOuGAuVfy=`ua~Z6mL!e#)3~F^BXr3}islI(XcR6n2srwE{{1^v9A%hR zYaG-eg7y38QIb=nQ~2(~aI|YdSs=cJn*w<C>;b5xkl~!8+_*ewx}Zl@$f0&F14rCz zd(uQYAzL+X22?fol#EZjqYj11QT>rEYVUiCBrNNcamQ1n(5c}*!^pm>jw+7F>EUT! z&J^Xleu3;(?v%dAEGJivI53{HSJnjt{L^NunOpg=Fh;C%F%QwUPeea#e}8YFCMy-a zHaE~kX|EGWJi`|F^J#jW0)7$vjTUDfB=&}<i<g_(><CGp`qeXv=1t{Xm}R{vgi6xC zDroE>j9|g<-skqr?`oT#HC>5=VMH(iJt4ApYhuYe;W6pe;Nh}PKU;rFFLdaBLwIh) z%{_)F78f<aGDvw!iQ73E#MbUDZ?+RDZ#Fd9sRlHT8<ZDL_vYUvVx2J-dQkRj_M5ge z2r(~^!TtqaD01`F4l{-PC#P!w2v6Aw@Gu@V)t`(i7mqQZF|jkLc<x%%amu_l?QVFe zR_A@ze6+4rF1g<Sidg$o+hUG5nh+XalN;AbF$>?cW_jfj0XCC7>0?2208lhOwcH#< zggEObVBrUX!Yv<t?%-E0Dk<OLkNPl|sS`aH+W2Jpg`K+ylP{AsOlw98J?D9$G7=Zt z<6QIcskfn2<#j6<0Mkfu-J)TWGXNM}v0({$X&~`o_~{(}G1l>rYF3WK*T)bHRMQXa z9ZIAK0q1r~;QgPX%;z8AAwr{Dg}MILN((kkr&FE_Kp!bHWx#Ta3Pb6q>c@v!)YF*~ zFN`oqiQL~fs}m}b6W5$NV8?viM*1u$PI3vXRD-#C3${#Rp%(CuE8m-ZolW0_j|`MT zo+qQRL@OZiJOt7a49j08EPOvVT6jECF+M!lA>`VU0}qxjV=6A5CKKE((*M8@y-+NF zB}bDgI50d3#$_1x3rQTE#x7KZ6oWzRrr_Z@9WDdA+FU1cQ#z&oVXn(4hoQpb9;98i zKTYQgRvWFaBGZuHA5QDKWg_Z-+Iy})@Qa{*fKeAG7rk>C@KYel8;0X=*o6;-s6aU> zdHRVuX3wUV?>!Jjr&0OLWDZeHg-rP=_)>Ip{*3mu3sfuPaEJ|AaP41!dWvcu&P0)L z*oh*8>V%)prrS_NLS&W~Qdy6vLfjBN%TG`fg@;YrC*5PtlIvo$0kb&Q3>f4QKKr0C zRD^*nq^VZG6<QorEK#K6jbf94(-f4#KAQff3o8%JF^aJRRH=NtP^oCjFVL0jy8ObY zU07Yn#3)ukK+JGxM|V1CLo0>0S1#R?rlTcbkm@iDL!_eA$gbJ~(a=<K3q-|Jo%S-_ z7RKyjD1AM*Nj&oAavO8Skt82<-S<MG)ljnaJeoO&rRqQ3$ZDkUh>>gz8HVBU$EAoL z{iz-)S?PMr1!3sKqsvLq{6htlK+@<yRIG8_I4vn_ObCMD!Q~&Jh%_myrO65AP2V7* zlk&Ng!UM(i*UO5uAKr`tT_HzEx!2;WQG>Xtfz4Z`86`1`Eu?MORnuWNnbqg+(8p$0 zc?8>Hq>h-8RGwtTy)S<rVxU8#UUCba_-4C|zxj300A%@ffMcNfKhoOfo|!)QNe4?u zuvyl7hG`|J#n$J!cm+Vua^5F#WQTL*4T{?>DxN$YNl@;j&z(y!_a<^l^9CDb<rzE< z<2^;+cJ8J5Xn{5b>S<~2H-;Vf2V$5yf&r`Hs->NfE03y%ik#0mA1wvqSOv@EziE?a z=fzT_V=(pw9p0WI6;advZbvQy;8&6_5?^j_{3LW{-iQ`Fn=vkl`NZ-$<LemNfDZOx z6<f=V6NQf$dK-L*R4yHrR54}vQzza?iu{-Zl$+ffErt6qlLP&}dPMjqHvSpo@kI#n z0~3>g$g1e0*pP}!OX}g`bcR~uHq(jFNDu1-Sq9o(B^l{-f&E6+0d{_wm~>T4hz7z2 zkskuCK|_1A`vo28h~P0?qw3`OOaG_UbT;S8lnNrZE_^^@Pz&RD_C#(WB`+-XAsK|6 zfilJKAQ=~HV}N5eE39bEAJO}e{|YZL1RDN7YL$_TAh){Df9LHFbpYi5Wf7^g{AaOP zM}qQW*qKs^@)f1~(gRi;un(>TX6l2Rtz)Rya^*cIPkV?`)}YW(r>!O}64*C{Vv!gW zf+-}5-+S_C>|MW-2nI@|l&5!*4rq$nr0XPiesUO1zuqaWuwsACy(u?UD3xq<K4ToM z9vmN@UXPp-I+||Xi4BqNJj7EJ%;`t5v$>rk$+a^47FsWB10p%QTL??Zv!!nUKBr9i zYfHl0KQ2_8A(s@Pf+lI&Fob(6x<rvXcH&k|YP>@+Cx*=;)}(xZpzv$@p4$i2Q33vp znD0x8bR7JnlumXgWxeFFa#@InE&$RkJm%$)y12XEf5rZ?!N2%%<V!hxj{F~2JEeiu z@P4TGg+u+xxa=g$N6E`jS<i{c$d6;Zq?vWPrg_+$qAO=*)x+a@b}-8(FD=Mj4l-7? z{vuzy4mNoV$Cc-d;YUj}^xUctWkN(U<l|s^>9VEdn$!D2|4-v#zbND&PL^$ql0q<T z=4&PHlM{?)V5MJA3wmNg$ee{fP>R9YQ>0=N6Y~vWE~8tt!&_nNUilw0?WaO1=FC6^ z86Ztg866!9kotT)ETBk$LHC3#49l!_BCrmt8HAtuVJihkrd3^Ummsd@cE~a!XFXqX zZe7LWo|E(KzzomvgV7HQ9?MY>7A#9eR~xDkl+BCkiMJUjj6|?E7eUBV&Odb;*@>Yj zh|lUQ+Yt}`Jb2M{LqF)oq0PmFBQyhm`d5X=-MOUe5N8&S+dO5G=v{eYG8|KaaKVUX z=-b{5@g3OI{HH7+-WNualK%Q6<Io*cHD?`eQm=0sFZ7LBMuVZW5jhV2QoB?0EA5*+ zprP=(07xA4ea0w4>H6%iI6!kbK<Q^&W11QTEqaQ#l}3^ddAK3_Ow0-D8Jf?VEB@r< zlxr&xruscT_9)pMRr>zqdG?n9z1f@`-5((#&pE_E9}7c1OOVfHHIgSpMO9T?p%|+M zNI~CDr`~<c0y;&Es;{{9R2g~{cfS#Us5ymMo=o3BcO+=9@pzbeV&*Am65xR2%yW#R zg*1*ctr<cx;z~`I!Ei!g?lTJJ#m|U_Ru0ew`CULw4uNuVibs;Z!l<$R&3yt|u~vL$ zFWBGY8Aumdr|R=i`D$gou-(Q$XMWerfz_isdq{Y{wz>Hc!RAN5)Gm;g03KKb;7JZ( z1yGkR0?nWT#cayA^-iV^d6mM;jg{rTu9pQ8rexZwNo6{G8pjhUWpSI22&yZi0NAz? zPrY$J8IvkK`u)vP^-C`rCh^lN;NI$X30RJ5g$kKU;4fD>{7Cz&f#?>HM}I<NZh&y} zF4*&YkKzhI9dUpaF`{G$%e+fj>g(%&DZ2qeX+>EA+Ga(7!{>kwlZBA1kM#?pH@OBT zruETHWjHz_-kQ`Y0n3k>-_hb@VDvp=CT84fzF2u;>pfATF8ib%8Jm~-1Oz>b6fqev zo|77fkzZ3k*9}&P*TX=0k?*dX_?rE&V0=PCt6(2Mu6HcRF3&-V#%=<>ofG_o2vv$C z3jvj+e0rUwC|uQ^T1iyd%vOzpmLL8vphIzG@w@Gj^8-mMzVUpvzc$i8Di#bz2u(D3 zohyM6K=r<Rz}oUJ2c;>kenivoZT;4)qJt-lsRrDBs65AxV+%x+O*RmTt3<XcZgO_t zrRxYEp<qVVnfXw;+;W#<N6ulI;=)W(P>9KZ&0Oh9Mt4_5yGMjeSrFir^!a9U;pr^Y zGC(?6fg-?1s%Ln-2JRlAd2lY|u=HAYEh;Pm!Ojq4B|8LNjO1;~Fg2IIEEIv+WOTAR zlBL{p4Osiz?Vd7=Ww=nQKlmg<DtY?*q2A^R=yo=TZ@V9w$u)E>q`pb^(Y@Rt4L(|n zSKJ}ThG23pYwEfto^1b=iUM?ndg36)EyX@Win?*-bbnEQ^IhXnKXb$ATNUH=cY~v6 z7w0Yatoq)E!EoZ2VhTd1YP$A;-tUKAP4@x<O-^q4f@{xM+M&Jai3@`Po^};c+c`&> z0G*rxHvwOssG{%T*P{K((5+=94{gI(@nYO9j@<^YoyL!$da>iz>x<PVN?!yD<~A3Q zuH^EgSeaR{4Y$tj@AmH}xW5^8gyaA-vIAZ%f})aplyGA<#Yw{u>(+4zg;o&xee|@M zR_P&2^w+!kZTEw9B3BCM)v2+`YC*akh7VAA+i+n3HFu*ol~kJzVTi@2(`UIqcL8O- z$g)UPO&r|2G4)H}uAU2C`$Dg>jeLziVpE72cR#{4d%^N1-@w9H;7HtDgoQyl#UkhF zYz)pQ%&+rsmzf!NTB#o+aC|e_F+*6~q(eB|%kH3_r`7Y^#X?Ja|EY)|a3?DjvDFBg z-#~h!jv=o6%H>x4ThG~pJ>^kq+xz#9&F`+RGjn{f8>=q_ey-|j9lnsJzitZ-?!%t< z_!KnkJH3CsTk)IcGcuVc-3J5$B^{X=1!8H3l}6$p4)jFc97l1Fm_d93g5r9aw{O$Z zJ~jiR<pcx-ytdN1{J!1lvIAtIpJ7WW3cAiiuX5_0H&Im}W73p(j9c2{a#nweaN{PE za|TsR6ywmLVx9!)B*C?MkcS?x(N#Yer9SUyIE};AHa59wUAke6H~s9AI2^lLNx}by z=o~-3D)9CL#y-iqvu2jc2q8Vo+i%kHQlP`0XRR%Gcd?4-AM=5w214LkDc^0<$j^6d zRbrH>KjOS&5~AE){%%?2w*b}N_`cdqM(;X8K|geRSK4Davo)8$!lbdiY@*q~VKQ)( zHc;p6)%&f>SHxTJoP$XU<YSk%>cVW_w|aiJu8xUyI)1mOR#_b8!x>9OYeDK~(ZW|2 zuE>LF5EkNO_}A|vU;~)JXGLs(LeZZTxR!a^1yvr|{uK3kRINZrZu%t~jne(~?kW&H z7<t>J86HYP{26BlTI??392S4;9}!bvvl_oJpvZ?|Cv3I5aOIphSZYJib~Ar0@7iC~ z^GJ}KGaS!aai4S#rMwiEVs@yMwx0*Rf``S*1UFpg%Sykie}}yHDEN4N8*e8xXaBuq zhKHNd6ZQIJJGbe~-PHrA7UE#K#-Hob9Vp0Rb-I72EA&-77p}l;!K5iWgsASXtBeNq zOBv(!-vwywk#$x1Br;hpGBE0w&w}$*IVM=bQGF{t5>U(i<bor2uz1co5-^$dA26)W zosym@&p8K<G|@ckh?oHF^a^XVZx&<ET>yRg?fD@6vA&~ciRa>syAs2K`)Z_8PZUMX zv!3{8$FXs^jXm;dT#P3m_m>JxnK-70h0Rg<-Qe>7`PwmY%R<MLQbJ5nopWSqd>+XG zI!W&>(rv$w9fk3!z*b_sF+%n@w@X~K)_gg%8zegTy$2h{qOrKn5<@v`Z<3QLa!-1@ zuOJj-bI#%;O@=Z|qKN{v4LMS4K&+j5Q_fX{fHJR*i2JZ)Bx~*FzhN4QmVq|UqqsGr zwcgkq&(j@CG>Ix+kBi-kmTz*M8=2i1h7(*`W&1&R3QBI@d8s&_1+Jw9<}C5s1<0ku z(3;A(JpKu$Lr0Qwpwj$=Bc@xlcpuFw(s`5_3wpoOEQdXXcqV30DVI(mk?gj6VdM0r zu<h<mTdyefQHqQTy)m7+*xciAXoUA29rMZz5fm>ky*<ou9*zYKt<opI0GAYq0aEc# zP%;r}T_+>a(P0l&sn>={#>derQ(4DITSg|PIK*_!Jss4;!{aFX{zmE^iiYLZ6r%hS zp&z;|5U;ChXe2fK=3dEuYeCXx-x2yw`1VA#el1Qm@9S43&~=uawXSG-7-mRJNq4#6 z(1G=9=FdXY4?)mCLkPdG)5yO@^#Tw~1M1m#1mlRy%A%Bh77ft97J_c*K3M})9a9V| z*X#&1<t#3_?B;k`(-ru)^IjWuLIB8g#EC2bgisrZQUxeKv%L05q(S!tOHw#XN2#-- z>FjMi08?@O5l=I55ds(6RSjSPk8%txCjNZ;sZa$5PEctM2SAIzFyhG`XbE5G0UZnH zMToL<D!*$<l!a1s{Kv{pj+J<R9)W<x5M^U+FaksF^`{bQ`-=<FwhkC|Fy01698DC} z_xk&aZ|MW_Hz+H38Uc(XqDYa1Ea$WrJh%mQpsz?d*X0E=Pk9)Ise}n6*ew%`f<+lE z{c=ONp+kQ(fS_7ZB96<XIHSc*zbp&iC@2hjj>z#w*3>R|%97b6MAU89IE@wcaaoQ_ z=-RBRH0w2aRso2B@(i*d-OdM;)`0c{Ahz{_u_XP=<&RV*e}C1GHiIEhiW-1KguY2R z9W1q3J&XC{dykd-Jmup7h*#O;jiG$M`@1V2S|;${pH==fR2BxvXAAbtmj!cRV;V%v z28lCUy2vBlNgs5*a5>o;6BD7azP;e11(QwDCg$e5q^}v2;A)0+uiW-@5`mhuQMVfP zvoR8Vb-(2vOmQBDFcQObK4e~iIUJWN@Gyq%I%tzl93XI2u0iXE2jkV|iuFKoTnC&f z3EWii90$B=IsNlX`CPpC9Ep5TPJi_l9Or<s|0;vFhpe1Sh>=)Ci-d^51b?Yvw_MeE zz}^4o0C3NE@GM1<Y6NJ1%65Q0`~22ieRY(EvK}DV`5J7bj%`Wr>=-*juzpr;1LwvN zi*)ah%Y)Y;;h}lzy8X80`D<$eB@TW{K;Pt=q7(_#Pfa$nI4v0&8HWNx;`KJBh7^ot zL#w4$e^Cpg*rcR_g?Nr8gM~iu6hSG3WN^OSWebc7jKKy+bHvx%pRmK3)9tee;_2nY z7d<v95EHKxkKj-zpy(omVf9Z8MD|Um8q7qIbsxMV1K`?=pgF+~28tZ0U#^7ZrLRR= z$^#?T1hjcQ_Dfxoo&hrQ<VJ-c2?0Tq&J5bhS+tKy(BzkQwHJa+<#0IWtuzzFScH>P z)s(zvUz(&xOcI(Ev8xG#5w=PMaIhac;+DeQ%|hdOkDyFHu1<G%IfUPNK!<4;WCy%| zy*%9Q#8XTF68DzZ0|I&o5uzP&wjr4{;{43Ku{0VvD}7fP9obP2JYLfzvU!Y6J^=K^ z-DBb2SjQ%5ZOKiVRUN<ks`719LF5tjEV$1?*k-h3H>VMQoBm)wXik*wk2HmHQcd@I zD69lK55)N^mP(VHuio-Tb46)vtcF`B(XY;ppBrblJY|GX)?q^z0<G0x-TDi7sUy>R zu!R-f=cd9fhsh3W1bsK>M3Hb8iEfRnYpHH8=d5I0n6P>FCadhmJrq6V`cO!+`_;jZ zGG2&Y-3EAW=9G^w+gO?^+wayxkbXqUU3*$WW8s$j1!VEcym*W6_6H}`1AFHaIHglB z&99E=Sz4jH=JV|1dZNvwMBlkuL6UP?5p@`h|A0tads{CH?)LmP23LxKC?Ue7&fWc- zxuQ{Juld?85+`eZG5du2&YjIqekI2JVR_%>-Brf!>P!!70vodTjim49T0CN=&qF8P zc_v0uFaDUfux!8aImz`+@c2l(P|P7^{_#`j-4WY*pSvD6mAxJ;!%-n`u*djYRn>U< z=3>m?=B9<opW|e=6KK@=dSLP;$9?kkF<9a>jRGV)2MVQ%zDs>>x+c5vj?!~0r$lGz zdlJ_g>7Agyh?CDSZFwWyxKF<qz5^-7$DD$F3V7p8wGu}ws}nLfF_@W_wgELQ`I(X| z`_oK*vb?2yV5`1<Ihy;`E6Jn^t>WNE(2)E~S+mX6TqPsoEV;dCnrdW2{Iap$BzFCB zzsuG}czf7GFeJ2@m~<$t6WG$bg<?Q;(aXoC(yd?hIuCA*C(?*V#X-fo-~W<)-_*M` z7V1ao$H!iE^c=2?n!w4P8?$~RoyyMD2){<aGt=gWG;m6O(*7)$^FQ=i4gp;DV=75? z){|fK6`gb%Od1>(*n7H+bLr$w@?}>&v2^G7NG4aoa7MAy{G2cAxxRv36et>6tIgkK zBYu^S(#eZi>k6)KZdc<ESP5wVeLMKVb#pyr<lVXnoBr40!PxZpAA5rBx*XG!b#9j| zEY2gHO>>94CVPTS8%$pWA46IQ2?(q*c&s{|pUXOKi<)NXiGA<yQ+cTE=^4`h)si;q zhwH9};Bve>{VigqdqgblFr&2+|7MM7A==mjdpx@(Po%2%xH03nH@mm@-5o`LT<pf& z6Ax|Zo-DsZi!{k%(Fj@ZMRz0oMoM@^kcrG>jcTQ5Q$DNq8{Fr<@ut9EVnJ%-_=iZv zw?Ps1>F9(*1@CG-Sp9N~`U<6XT_ZYA46r@f@TzYt^sZ`uWR>(z0M>n5uc-|k7C!?; zq2v#ca1s*BGgfEBk`=9{o%WMs%NOb5b57mq->r_f4J5JXJ&&AsTOS}9CvaCdQUMg? zL~sRH`W^dfnjqEr@#djmZwlE;cBTt`IbYPZJIVDLQTbofgrQD?>oxQh_~M-}x$Nvq z(|-IWsu(jsT@$h*G6a`LAlWaE+5|3KHdA?{Zk|OB77Fa^f&sR^+@QnA!{l#A)39#G zHN}PKx($AE<UC8zm*iqq<`T5ryDH3Z3ZHQHa7dK>4U;DwSP6cTpK~o7j=}g*cXdvv zI!Sadm2fzlK5|~@gZud++IEMtgzu&x-i7__Kq%S*=aB&Yod-KRq_J^7g9~L(bINVl z2V&a!<WNtUL}|edjL2Vwa-7{BJL#;|4hr2ICna5SMZ#cL&mk;F=@JV3UL@OpMj({l z#3M^1kVe7O7Bdg%LhM&*l99j1Wq#&&M{=D)`(lmiFOCm;UxjJ1L$DZ=>v=!~#G<+D z{jzbWyB40VTkY$q-=l!;j>jf5TSrXEASWluCbLWlW>){|$`uHybLLa}LaC+MAy}l2 z0~nm($*A~Ih2O2LU!nO_a)^9d-%jE{&CBFiWyK`6G<w$Gc8pIphvVp2-wy1o%jPS{ zl$cDFvOn37`*~~to?IA@_K%Lmd6cdfl0Q}#JCr5#?g@vm9BEI`SZU|WlN8m1!^1J+ zo<TGc?)DREmA(fk(8w0hFDiw%^fII{KDu;xZmQOx_81>SjZ5bV$#nNZN_%p9kmME^ zDrN+X^@}I5X!l2j2d(x1F2Z*Y4yzq~{1$**^+jkgqoZDsXn)Y7*PUC~A?u?|@PgRF z{D3>GisJAC{8)*Q;gLJfr%y&S9K*RUUz0zh?<6}^Fx^<Jjk4~nb&1T(=kpjZunQT) zzGHJqH1jDWS2186(TCXXnD+mftRJ81+Yr#UlNh#N!>If6-G=^NE}LdZH`l^8)LoC! z_FZk9C8t^c``<RKs)a)_NvvtxKAL>ApIvv8pg!GYueo*K)>yEiuw`lfxURLD9qgL6 zA=2M#DpZJ{`l2Ja`NMs9AenF|y&tY!Z7M*YzGCvO&g*HGehm9i8h5OA^=AL~clPTh z_q~L+zw2aZG=EmSS%pEp_5|HpzJQThzr1-u+gI`tI$0KLN53;(b9?lwSuRGo>*eo* z$2E0R1?>G7_AC1Q1ZWur#}eIhK8DJKUA(w;T<!~+2`<N7Va7TlI@bHDX<m1AJR&lU z8mEbgZah|n7%Vy_^T<9~C)=K>%uTacLJty@^gZyX1jAgkCTkF;*W4yLcqH2+)v;4* zT5F<%I(1Z+A>RGR@kFu%y@i61MXbvwXb*bn6tl_}De*o$Akj-apbmn2OJ&iu#G0q( zeqa?S@R$p<_jg~ta_8u{qZpA$nR}|QmN4i@J)E-np$yUU%2HeKR>Y%MP$R)uL^O^^ zWSgbB4^xnjlJ(xpxrYigllblfB-RB@Fj<Lm-<)-9>VK6g+bU$C<*c|D`9xzlA|cjn zEipv?ZC$@tBA3NP=lZ0eW?x0@6;^uhXOHUqNy2x1pD&&B3|9-{_L<INA{3+2d8|vi zsh)n!6Ic}9?s9$O*Z=$|!v5<K$ZZKv$IDqj>JdG{1VASO&bvW57x9hOjyKD@<7I;L z8WHudH2ZjnwQ9K&r76~Q0ZkzMf<~xMHVD^z?SY9-y!UIp=ktt#4li&Sj~6j573*Qb zqTRO)1COPK?e)3w%>PJD(dxlcdxS2A-6_izo7eb!?p_Q*cArIU{(fGbQ~ZN2^GQPB z6X#>oCz`0kE~W+pa?@MRjtx_Z{-<--EKcju$-(K{hBrZOJK(vm3%b7DMj{V_8@DL9 zeUMWYZ!`-sNYlYp{YcHu<LUd^E;M%>`m2J?SmOy?5iH2=gu{^eP~?U5I>UtDew&>l zjy=Z`Ne{fGI$v5!YHtl7m2bB=?^y3RjuVk=9^S_6t?`VPxsWU!8<>#1u5ahj)*xw> zhHhR$KXB1j;1O!09%SOVt+{4p?#~Ul>or5hgD#e?>B_)^!~B=o`{X3&bnxRwxcQp4 z2UWk*aIW`qCd0eFYQ1iOr7!S0YP~SBex;T0V|Su{nUy&2?Olf~idisLU2&ZkY)?i5 z$p(h?Nq91z%1eEl<Cys?ZZ`pa=K!T#*d}8SAFq&_sP3y78~cc(E8C@8bNnmUDsQ4@ z3;7E&_nkia^$h2c$;Ivs+G2b-kDj0WF#oj<U(vK>)%D#+nQ&&UV(D$u0!?1?NtrKP znqRaw62e1NVv<*d<M>r-MIo1ee~he6_Uy+28CM^uwv@N8FY0EBdN1mVy)PIT)W&{Y zwifk1K87-}3!7nllP1~x`Jv$R0}O49XBhG(H~C5{&0K$E2N(p4EErQ5=bGTws-lNu zDjcfrj#L;t!Vc`Y0h>Gkq4cQWDp@$&XHjUcg+AQ8LTyPtw3&<8T-Nn5R0cJy2&ob7 z_Tny7(~lB`d0)O!xyTul`)AoglT<aG)7bED+1Mwg`{OS4UdbVr#Z~<T9NXmvMmo<& zn#9H3D68nokv@wx?G5a|=xXY*oz`c(-m9(UdcR-i-lsy8yzD+zXR}2*CcE{DHnSa? zrMo)bHT~>-fi<n(T&Gcjw2o`kolR@i<kYk{KBtUDXKiKtFXY{7R&kc~0lRL)93!bA z$@wQut<}^zm&?NX9|irP=59j6LuF)pf1l|8?8=W{|8YK6=aSg!M3mf1$l`Lio`4%E zdw#aP=cAnTwn4`1E+E)x^A=~|n#$*M_#=YH(`#4snJ%A8ZQho$p37<G9QZ7?CxmZ- z0p7{*lo0Ghf{rO5P<`4^Wny4rV?rAb$LQ<9jck=Bu`G97Gii3*HC=moMDs2W4kPl2 zJ)w^3Wmj8@g&v_!M<yuO$J$%;9th@}uCXp;3U=9(eo^!7<xLWdgj-Xu3Noo?Meaw~ zXA8=%CWem>xDy>Qj7TDP@fd|ZF<G;1)+7yW#(l%@pbrwzlKY~;33MgOKJr?r-WfBJ z><oQVXC;usge#mk+lywEZ1rs~Z}v4rU?mh{GJGTHG?iZ~ja>M8G*@?RMO&%~x?|-b z&71j>B=t1ULEhh&Dk0JD<|wl9;9w|?C?(+yY3z5MWN6UE!8)ns>r~~p-PdvDc~mU} z@s3z4^8<C*9>L}`c8_y)R*AFqKMAIeC?@<|GaL`uk8G46-j<*|*rdrF+#q}1H)>x= zp2|KQ6Dx(tJ(Sw|F}1u&Ug~Pe)DSAX4O7!e+Rj^9F!C6EcivK^8rT5+G;kKTJS^|H zU)|p&gjJn?SzhKgQq|qXxC-0W`Q4XRcg^Csxp;bOANQ`pU(n&+Z|&)MCSHrF&!!;J zIc$SK#$=5%YasbSo@O50g{2gedS3+%<cT`gT><)`M)*DfiK85O!f7U6Pbc$JsrAGM z<Wk~k)<^P=I7e6>Ip5%$bQIDfNBu}EAJ4;BkVC~+kVrhz_p3qjX*mw>NAf+!f*daj zO#e$_cSo7W<gunm(0;V6NOxNMI8Ei1Izo(x`x4C?6ix8!L%B{AN3pr5evQ?B3(~3g z*mYFUe&w^aO;?l72bUjqV-}^&?>-ZIpRW@+by4c(1(wqHl``w<4apTlf+cAfU3w;D zzpU=KCfDlL2bYz5=}qUwN1F{NPTyzIK)`^rg21IoQ(>jY8&Z_O>BjADvbC)1z2s(d z>;&OL{gNhE!(+lboNctqDFSZ>0(`ZaO!!P+ZyB>+L-@&efBJqBFo>)uat!x%!H;Y8 zxG8vc;K$ce(8nNg*;H(Lv(87YL9(rW1xRTSR>O@G$wmSks7Jk6*MFES+2*I}tc@DJ zB^}Q%eqE0DQaI1XF*Xocq;MPjO(osToS;u~_=(*4sQ5WbDaYH5wiEJXzB&8k?x6bX ziuiiwx?MFE=N4r<jLboT5Wqfg=0nKhF0BdqNT!*08=XuUmyi2?FPZK|M@aB32XW>} zZ`J&zKEK~wICvR_j3X$G?I_ce={Co9DY|O2=yM*Ar@;TFynaV!9?f(4K-w8R41_`h z6BNhU3XmLF+xd|l&;7U78H-nED&&~J%)dr=;c(rE;Tu~;L7{oS$SmG^NSR)P!=m$u z&}J3Xsmf(Cf%Xp`c4F+IoA^5B=GC#~H<%Y?f~N50&bJa1urZ$#mD#Zz7x(3P7GC@o zKv``OI-4FQ^z`(p*Xp^==cK#YS%!{xp}r}R><lv&NeZB2UAwMm8HW`as-d~SOt6Tl z*&EKL=5@~#^xoOh3}iT!UCykl9hFv0`(&L}p%yb9-RP^XH)qrJJ%LE|_t9q&Hy2fE zzbV0mYwCMwqw094X1FiZRNd3`Y;8$$jar{?vg;2BO=r>G>KI#hu0|&-alnUgnv!eh zJWH)H7ioNIEm(>6zvmJ{@wv$YR-JTzWu7eDzsT3-^!QsWwZ0_o)%)w_>$cM807mL2 zJAuD$MO+r6(os8;@7{j1q4hIY)VMVKNX4l8enC>t;}Yd><nw5%iB*GH>wTZKRPdBo zJ?m^d{Qx@{+iWsVk-1!8fcVau?>Cw3!0Vw~Q$3Rs2_F1oty|q9@qY0%$chPI)>IoY z5%O13;WJZyw`lzZm~QEPHLXiOPc|8EeZHVB#bM!X`?z1yGic;y$+^#Qy`yKfySNw* zHV|&4<9<kc)La^E_uC$4_KPHEAM5|a(^&>Y*>&MsMGO#80ck-(y1PZB8M-^Aq&oyb zN=k-?A*30)JEUXiRJyynIeXskobyBf7(DaL-g~XR*1fJfwCJnFLi84u*I?}HsIt|~ z_0%{vIU#`vJXn;ii%cZ5(VC13tt!2a`iz5$MoE9pB=y8`Sf0;2K7y7L`#w9=*TJGD zihf3$yZQs|y(;+|f*9y;@NvcC&H7WDKW5AHsA54|?we%yYOZx_kN2)u;*GAQa(1iZ z=!)u8&uyBItv4NZ?8;VSwR$QiZ&PQ4P9E}{l(gxPvx-}}!XJcQn6BoPm_K)5q&<Bt z`ih4@UzsF21V}TX4M{`^aWE!-%Ug+#0AbWcc^Mho7tj=F+G2nLajv=XB7sIkvsmm3 zj)W9tFYXjdKkJCLZV@?tf<dua=Nc8rDJBXv;W}=OPr|hQ3AC7C#S(>aMeb?BezQ!K zrq@?aDjK<8gExlq@*_%xFw}~l%SO1c6n^iMSqhUZ2^&Xw%KA06^swWsO8CdYV?{Lg z1|^O>wMh<s=E0yM+@2s3iFM_;&YlB&3f3`c41%#q_aaHnJkTGviMFHY`9*w9Fj=C7 z)2GE8y7k@dYv0p+t(>YcX=!LBuMssbUKp&BS8d&(Nc*)jk<nMLNU@gR3px$PKd0W{ zt}Giey;6RIjYZg!>lJj5gNWPSzvOXKNDy=L6h*sHF=4qHUB*36fxy_IKQMXa-HAMN zi5zxHBb8pIxFXheYZa)a0@IXNiZtl>_RI2#>^Bz6&ILWTGe42DkS3$+xObt=M>y3f z3uQi>XF?<Y@;i|tt#@1nzBdzk^1}RIo(h=Eg^`H;wfp+f>qQ*H@Vb%MUR8mf9IN7% z1gHo6kT-IOQYObkMJ;MkZeRLTs+M`;T&`iFc{^0!^6YbCbTl3V-$8X)PShjzSW<c) zyM2jeBPZE#cM$`~kAuwF4fXPDiFLD`@_oU&fG2a}p%;M)G`r0If^5DY7mj9Ky~j8h zXutVLq~W&>2an}AvB2sv4DdNY24B4za2aQYi(1Bt-&FFG<2k6VZ@5XnWdeoVe#i(l z9(KdDRee}ymZ?OW7{o~R74!Qv(@EOVtu8uuL7H^P>(ryZoE}?$JVt|Af8U|UosG-E zF?6f>#m&Q#(@f15zGD!%=YE7A!2CgHqmbCUi@|S42O^mH2odtT!whdirR+mu%^d0e z2b1|uii(a_q%xW55S(!Gr<s#Qy8}j6)UY20$|QLd^;N2Qh-gPL)lR90a*8#^vM!hN zYj0FYBu&Up5yj0?)_WEj4f^5vGDaRw?$;#-)x8z&X)J#CRx*6ds6nV=Lh6;Uj%`QQ zPwC4`^!9rSCTL&(j56F!q93(w+yDMbc=*e`jhyN0=Y-C4vt-6`O!Gfnnj201`1uaL zl5@jZ=;BZmyi5J3Y;<k8jAQ)OCGtnw+1hJIY#DE29-brUYj9JH%$1)?a`+CrbrC!L zyv$~pu8a6fr<8G(c3`0J4(-<mxOrmJaCR<JwwZqUN1gmNFnVN%BbS+#wUsxYfS)ju zZKVYdwFqFr3AxQO&4i<CagyH|yzbU4V(EEH$LXuU_FKFCQZ$invZ|E^g&Z$N1~+_- zcfv<snQCm;326V#HG>($L(o^vS~jHz5<eA|-}u6iJGPGc%RnL}>Sb-2;lK2Fk7;Vx ze})3{xda<J896T<Dr;jLm84lZN>75YYd)EwT3ciY@`c;kjb+-2@SMf#vR-6stpKTC zR8rE$I{E54PVZ~+vuLkd*;w=eA)%cPM36`D>=-K-L+9Z4qoLmRtw4LIN*q@wgkg<3 zUcvyy25%hI$pbn)2C?#@x3(D7(dFc-9G6t2!Q0B_q=amZ+vL)Xbv~l=wzP4h^zaYO z^S>~Cj50Nj2>l0Pz_E9CPn?$aJ#n;)UM3)3ZoU!=5IPG2LuFlivl%4*r1R_RtB4~U zx~!613BoL~o3`rDwO_0)mPtm+ynZ~{%C0hZ|7vZaRx3sfeousHW)K)WChw=It$`P& z2E(Q{(5W1hwAds-GUMygGm=%L;0K#(ur((@S<9|kD<Pxi-hk)#c|OvE2H0)()DEY< zYpyvr&RT8Gxg%%GmWwNHP9!yVN+0an+;mz2JtBFr=JRlVFdG1xD!!nf#^+cQ51YCO zWS8fvblQ@TO{;}-Ea5h!722Q1)WHjlQTw}RRZq9KXwSgUyOPM`!Mk)rHQ=gL&Scqo zYBv5=H9=L#B4ZOR;)*85K}ovTj^BFURtXjtC-+1_<J0mS^!#+_6?{jln1*`DQvC<3 zodm>b9`n+hyy@XUwQSs0AiI8Y4%xXCTZ>Bbu+#_isy(+$x3VGLSD_Z4XU8Uri>)ot zgPDE81<CVaQ%TwySelmB7WvRjR_K&<y;YmF1&qB-@WZo0BIN7%Lt#WKpNbtL`C3tL zcI->0bVzp3a(0zM##Pz5u1D|O^$vL;k$nwaLk5>aD_QW*Mf#F1i<U1L$#|}Vh6a=( z-l_yj+?FpvNBMa@y#K|_m{<ZMk?4`>SP=t_lOo0F*U2!8b`RPY_3%O^8amWII6+pH z6v2m~1n|S_Bxtda0~vsf@ZlMI05h~zn?aSUvah+jrw6KWyjGWtr=@!67gAoA;FM() zxN96p%!hdAu}5QxmND4n=H_p|VGxMuh|{HAPxkg<@4AkCeVz-`w9>6(=rj!5ALiA` zBN@@ZYjEoT*c_iS>QVm;iMvM}51p@zJlR9HeCxhh8KkY$RZW!na>TeO5#~{%ids@~ zfX7kA1t-vWt%GS#UjY?FtHKV|{3{1swJlp)nGai#p%FG!p$wVt%|;U-tASh1lDW-{ zF>2JwbT-`kG+dui<6mweA`?`pBEH5~R7@SUozElU^gJ2$Jnw4hb?Vq7@D4-4LQ&DG zR!@_uurIwDwl7u5H{_~>$KbUbFlTG56#an57%1azp$RbPB-UOvnp4h>jlAo&>wWIV z!!-Hd^NowLeCvGC(*@Zb`8zunpxq+vL%e-S_@lU;Yy7Po^A$6btr8y<l^)$hZ|1YE zfZ4Gn1{0@Okqb5KU%K4@)u9fODhN@eRd|$H^W*-;C{hMbNVmx)Sj^hm8uZlg9h6|) z(T+;6b!>m951E9%1m8sxM!(Rx5srt|!JVW&fn9Gt$c~Tzh^}!Nu<xy=D`LjR9O09{ zOKH(c<#ntr;M_Nlk0GT7XE<`N?&ly@*H;MyLNSonV$8V`qqa)*38UqjwwJNG?}c2_ z?Up;I+LZ9g5ErgWQekE9iTfc|h95QExhf$uF7HBMQ!$okfd8ei3l3KE*`&=a=jgd! zu->5K{-TT|Fxb=<nK||gv^h%YI0*%XG;HjJY|blRuE;H0B}DlsbAeWM(OB|v4Uukf zifjdjO&r414;MjpMpv3<+^+S*4$P!nxp-OYW_|d68O+|m;*JF?!*{9SwwS1ZpYU!y z<JqghT3Jz&jZ3=OSTDxL#_mb+*kuqW85OT{+GhluA503d1|w?=ER>Ny;Hf%3x8pj- zJSfk5k&_e;jwjw02!r)_LaxrgNK~#$t-33kMp-NKIsTc~=KrNLAK(9Z`>T@zYr#;3 z+4Gn8sdBcF$;U3}2$M&$hLTyRM6q?7G1}5g6cbOgnv*djEE0EaJU6&j&XKjn!)g~9 z+x~vYJFd!jiGCQ@JjXlDy7^1R{5QQS28)_tq%dTsaj@w>N*~a0Kl=H4FcZ}w{({V~ zqc6kaNv4RGuI^ummHvR%zcLK1b5Au{dd(W<XDlG?C*O56TrFgX<gj=|{7vx7V<T(X z@B$i5x)59q!6L@U-p_fUu3E$jQRAvCO_V5o`LVJVo{S`U?NT-JJ-!}tsPXH>#KuTu zm8lyiSQvZd<veN{3IuIR9PNwPF-r%awjQK*GGj>%)<iVqZM4O313sUy*`wt-yUSpl zqj9eJ-h-|?I;O^pq_O^>xi+x?KIgs-m({ar>zJp37*V1jea)_9f2KZuPf#x46+3mT z!OMD2oXBaFXKkTnlG(A6&50%VvvND0&>}<Afa0ZS96_G-3u_C)rEJcus!Y?rM4?+` zAoDGOO%Z97=@(7ybUhMS8wcl-2-PO5L4;$$I3(B!oH0NiB534FNUpKh+=8MY0(2$T zjmAquJ0<jZ4=?#VHD|ut`~AdTvv3neftS&IdI!RSLsS=}Q1AEw8SZRI0PkLuMvZM+ zxbJV{<H;azG!(8u&Fl{Kf=k&K8SZH^FfN-EXTN^+{!d2W5JGf6&C&m2f;a9TE3S@L z!<%1bZE*_)(SxQXrL<O(7S$+_*hLxa3(bm`rgL};1?q#duI|0IF8eGRSjO_=LXS${ zo1pj`200GAl*!2sjfG-0lr3)j`%lzZYL{)0%hwsp;Dntasaf*IjxtT0f}Bl6vphwl zGHY|qUj?Q?jDyJn8s*QY&BUK?NzB$I)ET6_&5yJmqbC?E&Y73nohs#`t}G{$Y)VTE zjSVT(l&rgMnqJPH$g(sU&FUup1=Ebi2I*7-Y-$bPe1x975iHY5Z1daLIMcb8`5~DW z?`2X2b!NvH_3F5@pU^1hz%=Cs7rl&qUk5SD{-d7nGvE0h&T4+(dU*9Y{`Ck#Kxkh+ zUzMpE*ZU%+uJzN*7;6t7$Tm*ei`8TJk-^NG`dj!WB??osqO4GPbHPBEmmw4S?&;R7 z_!Di%Qf<Hy8Oe6q27#L`Uv=HAts*y~)?8Sh@_8Jk<N?OGEj4aj(Rwy#onzW%Cv*-{ z2xhX!pMOyFw?s1**7c$9LBvH%spAj1{iUA;L5$Fk00zrcWUexsR#hAw$@FRyV;OA8 z-DTzRcMaPt+;*#cvC9kCPb|SL{GsoY%S|ra%bUuLL)GLR37O)oaf6l~rx0>gQiy;{ zRdU&@4A>NJWg89+u9Ty;h*70dr{Ludy4%*z*Q|TDERoRHw+E$`pSGrO(Q)bJF-h2^ z!Y%@BdO)~*l1YhWw0WE5gX643sFQw0t_E^EwF2hz6tPnur$<f>b(S{c(vaaRUQMYQ zI7S160G67mz)bB5nynv18QThbe$7G{M7mtx<;Bj`m7>gilm4Y@jL3Yhld1IcHCiBF z6@X~5HmwZ#O9al16mLJnU)s&{{kgc@>S5>aqEt`10S|er2i;nm>IV53L8L?Za??X> zKOp7W(hi=dy31yy$)FU-2=}~bxjyPeF!cbPhe?wXdnjP$jFk=PhWhc*rr$pagP@py z9Ar9v?4YBQ(_u&%)lm<(G9ix^<M>21LF=RxlA!krj}rU@;f3rf69mv)&rpQ<`h1F5 zCQy5?onoMshxTbzD+UfX&(lzoHkop$F~&^BGni}Vl)5TfPjn7`LCE^a_;Yd4@(kyL z@ys7Dh=l%w%e_>pu5MfHKKN<(a1aAz2TEJY7U5NLJm+V;EgV!-&i-sgw)=QIrG#p+ zF+Q!nKWFa<IDMgb*!6IXjysGCn~_{yiKhf#qAlI_uE*T-BF&e<dgf%Z1J8XsV5aSZ z=whjb>m+BaGiZFhtobj`2`8?KsyLA`|4R`+riHMIQO3?2hZJUHx=>d=TTOQ*ei0qi zZRA_{SCy7ugP%y(-Ak-L!1T#dGdP{)50p7Y<Ex<w(Dw{l>_$}2Ao|+c9M&@*>s+>J zpOU!86FIj-Bbz_CQ$B1=*Qr0NV>bHoC0iDoYbn*q+Tv0i<Y@Og1mozN3$tTYeb0(l zh^?k<v3vGGPVt8g<Uud#r!?^nJHde|00DIYS?Ox`Jn+n@%aik7Q8P2+WsbxA$v;@! zmd?vu7l|s>!rp41tAL<O@zme&Z08?R5htyyn^K@#DTSU!H2IN?gBC_ssS|?6Raw41 z@Za+$DaIy1X^5+*Wq}%-o^QVnRzMRfI1#_{tsA6pt7_UbxO$#H*)7+w*~s@a*0W>1 zPfW9f7poOQw*oJHj@Jh#!%GrQ7V18Saq4gfujF*aD_PW9;)^*6&o)(+$lFf@7}Ho= z&?$&tlWUkUb<j|m_`vzr{5rGA;ZhM`U`uyv+>+F3n=5+WJ8=QqU)g-Bq3}xuVC&ov z!bs45FjW2hMCJ7jU@a7jy}l(^%(IJeRWeeJL#z<SfWug7yvd4xy615;f<B1IXKLQS z4&Ts;%FY^(Ep8@YBKdaVqjYpfg@|Q9rx4>u<^fL$Oc*q)#)do2oQ%L-M51kgV~f`E zlR2KsbK+(v#nDVru|)PRh!thQ3+h38?K?`QIOpmH<DNHe>&Y%Fe^4-^pJRP^oYt95 z3IbFDo2i7ZZn(_tGOqbh$<LjdX|Kpw=q7d;c3grIWrxYyfnK%fos$-E^~1)wx%!79 z-Cv>2(l({Ny;Gc&CbPxaUj<WwT>2cqU?sEpcwUU>`EIph%@?EDvA4aw;~?9Q<-rCW zu5qi_>vxK@hf{ExN|Pf!?;ao?qeO}UW%0O{NRas;SpH7)YcmZue`m((?5o1-U#=E` zEHz#n2d4;%dWl-b%~h;Oj#seii(7dGr-{@kPSK>j0XkEAAf4>h_1L8Z^rk4J9<3SI zXjFkN`tz(T2=D1h-v(;!^#_$!4w_6Qy}ZKR<yu6^?{Prxft~a9K+TTj2R(Utim}{{ zZc5F*=BC@2f<36(zQvuo$udpuWb*!(Q`Q;Uix_HeT!*WhV4nf@z>vq)aziM^35ir@ zDDxS)3Df{urjoCUQ~z$&b8k>@LGz{v4S&D=*jl2~;jq5pI4A^WQ!AR{y~o2-*`|tY z9>`ug*lmRWdqw@Wc8`~;>0AgtTl2Hw=HiFF@*uf!p~wB_C&x{qA#V|?I%g_a<%r@2 zvAx=BS&b%IChl}Y0`~3*zQ7q8uDJu7*;E#zfC`&=U2noe1o#q2(c_9D=xn^D600rN zn@Ts_i$>YDdjDTd6A9<|Z^}*lLN;v6$Nmt!zc|a{X5BNJ5qaww7Z*5et9OXn-J1qC zVc}b6%^+otun0bw^eh9vo+`HeZIBn&aqOWy>SY`3767H;+v$pkam#c%7(sSc?~mpi znn6ekZBB|^fS>j^A)w^868an~hop@;ZcjkOZ_K3eRiu+)#4Y=N0Wzl@>#2wuI}(ZI zwZcrh^4Q3@dQH`0?GKl=Jdv<+Db+%CT2xfGGWQPF*+Gp;Z4*?~T2|A}`J=8uz0Iv* zzTJwbI*ATB?&);V5M>cvAPL<Y<&zQJHaR&V)2g-4dO4^<p#|6;x-jM<NxUvHQ<8FQ zZ<2xYt};E%&iOLqtb4LH>p089of&le!KGiz)!t_#tgScYaj|v`Kt6Xji-<aWp^ZSe znri3ShsIsj-2K6Mc`uVpTgb|m=>eNqd>$t3uI3l6U30^1e<ZXd^_FLGBO`P9jF>`M z9bc*T;>V-t*T04C?!=w9%JP#^ozmE?r&?*;h8tbf4;dm&q~kWoJOE;)`sdv9KLC<f zu${3pno&Jw^S3=FW*Bl~YV7iF3H9jmn!Hkmf6))CVsbcFa3kmnpubj-8p|)TrQDg{ zv?MdwXd=}7oQG<f_hpjbS0?$gm@RxX2S_4yGdvjn)(wCRDP`YY1`^E_g;m(d%Qb!y zMcFo7Y`TM8hGa(3!Bp!wyTHh8Msf@^$#v<cg)@YE7n4`RJay>MoZOXAPBBb>#^GGY z{@AJ-;q~+!x)+#Ax?44?+h|qbq{;BRSxhWo-t_UZT#ojP-74H39CHXIIe{#J9kVv~ zB)WJ+NT<3<)?y7LL75+23qrw~g4gr=^)Y}mbPJr%Q5lIfQw3I<(W>p0M!not`|@}> z^IR}C{pxfzA#gtm>x!7lQpYE{o7vaL-npA4r@Jb-D(=oSmFX$bkj@c$`Zi8$v~A0c z+tFWd5`~T%q@)$fN8a?$DNe_DqCu4PAa-%2ZkChN_jMPuuibRSd;96a>Yi{6*3~&{ z59!+q<x0`{S5R`iS`5M-aigwYfLwbgfnjv~X?+`c^)Kc>u6Xg!8*_8GDu(e9J_d$% z<H_Il<`uY5PB&d~BjYzfy8^>p8g76aqjGt61E#5va6ya4AVVLg$5Ky-?V^qNQP%!p z5OP)wRNzU_qEx&j=B<&5q|tKOA>+wc>)sdgq~Xa|HS_O{Jh7bV)N79Ui}^OKwJJsm zTg#c1j8Mew&SjH9Rbw;kdbngxS!?BbtfIdQYp^HxiGbsYb%oWmst18GmZO%*IQg2D zv2mt!{@eb4&p%<|sp%J~mnt@0?s>TV!>O=o;8bKC%{c!Zn~*?e)Y(AQ_U_`p`BGk~ zFYQ8e6xa?!Qr@3C{!;8GJ}@7uki^=RG20~g3^EM}3jk%Gip&<R4ez<k^2TWO@a<%C zs<0|5X8x%p>_#959BJ`^nl}niZdLi{zU9!OiEd96W`Xl-q)3O)>2zn^d*o+8I*a85 z{j3=s{xDP-trKYf*RX?Q$}BoqH!F^Hd0Y5QSJ9Gdh#8P&E&Z`)Y&2+3c0SM$Ui>|_ zRoafsD5H7fth9|5PGPU4MHWtb+1q{|*%4AXl%p@U$hJh%H}^55B0huzwxWaeJ2VNm z2A*8WE55G#8HOwvd2m&ZIRz-_{MIi30rU4;wBb*>U+BP9YMXbsYRZD{5{cFTPgE8D zCqq8^X@*ZXjN3nXJ|4ZyK=BSlDswGA=iim62xKoq7HEU<LCMHq`HL(aWD*+cjzNe6 z;}s1t$=&jCTrx71h)VGX?1cQB;tyVwOH%397Hj}y%5J7ZDY$8FKE)>@Tzkqnom(Ad zwkV;hYC?WTj&4ov1zz$`lrYw{=SyDoI<qp8@b+f`;<ICQ4hKV~pdNY};-WN7|AebD zAKZ3y87<q>Kv1<G7R7~g^_nec$g}=6`@wAUBYds-{!VMHFCin@D#l9u0}4>F^})F0 z(2kCcSIatzNMCx88>So9Y+@73e9!z-HCNY<c{pI3MN(&K$lElvzOs^6tAuPaIhDD9 zwoWZA)iX(3?Hdm3v5)TO{GW;%_vXO{H<gsNJ_&pFc8V`g&4y<;lx?LogfX<vmeYbW zAJkS_|2zwDQQ~9bk_*<Z6N531`4ZtfD`|Cos3jOj3p=YU^3sh*_KQ~YYxOEXNi8QH zpkK=`jut%N^cBLM;)LX6^xS+-Tgq0y^y{pGJ9#HUbl#kTnT=S0Cd+hf%a9}c{y__U zdW5vCV`N)3U)8EVI!0hHmmtW6K;*{cBC>gR%Or0ys5kprqa$?!ei11eXexzUnk_JF zj1D$O8;FoZuw=|*%n9%wu$DXaD4uqf+O~XRgG#-+cp2{vO<W_0RxRk@hz?};w3w8H zW^bDNT-rGPlsXP0w?j?Tizxm;3Kr84G`^SUSscrDj^FH<xO8xS{+GgDWX-4ya3M)6 zwuJxvK}!e51r`b{z>~nx1}27&_5_OylV>I3%`^a~yayGI7$n(K?V~XTY04kV2WQT5 z{C9pU>#M!JI0(Z%D-k}E^0~S#Q&IVcjAN)-C#K35!OC1}W`%`)J%^zlY7HiRNj%bk ziBBbTdy<i<nvOyx&=~G{w%K*m{7X>zmv9Xx=MqWN!wHnB_!!tJL5$f0JlM;v99}02 z>6Ghy8pR&tG`rYM0!X1|^~+B$jE;X8#eqCVnwPjvSiPd;kMZ@B)6<zX2CXM)u$R2a z3DobLDWcPk*>g$k<fHluCL4a+8g@1Wszl*Gssn?;tKjsD0~2?0fMW&ysQDVynF=4W z>VI_IXo$TQ;Ed>wHW;z`9`#y~^ckhhF9)q^1J(}IFsF6*QofG?z`>yH)#siXyX6%y zfENkq4Vq5?WWovby6U8F#{RT_c&nW6teLNpJ&w8g=h<hSxS~o+(bRW$5^<xBzLv7q zO=o*l{CmV9WYO@-m=g0-<`zD4>@te?<N~uQ5m`hJlp}5NZIT%|9ujbb9<4ngZ;WA> zRwt)!{{8VSERH*aAgxfPSXW2q+P9FkHh%c5mWOAn<hv7$lL`zVTU!1$X3m2aM8owz zw=!=ck*no<sRp7Q9(zy_x%nfkgI<$;YZK7=k81BU<%<Q09{4BA*^o1B*DG=l9r^Vw ze3wh{iVu=*I!}+!(o090(Lwyak<ZTsVFY$75?Bl7fAg-sG=uTrEV)#{tDQJ)&(h2s zQ;FqlhRO%wsC@;@7^S@xSBJsrTW=Y8Xrsu85T1#3GDp_KFZiG(q)@wgfUCvG@~AFC z^4Jgx0v?ici5t73Gz3*s?u6!`*f`M4+`RDf;c=W<AT0~7CFl=fkzyorT4uldt!a|k zwUQkh)12@__-QyuyH~V$+ieAeQwj=TQ)2N<wfp);ywOJfX9hqKJeG!*K?vwy5>n;x zQ|uRC+B3chAP4s>#;A2z@KJwsKBhZQaTwsS2NShV#BGA;H$(0gK%uL|I1I1S8au-> zvn+xk>Vr&#FmYR>KJimX#Pg`K4s*PYb(#NWP;F?WoAvE6vbDgpQ3DZJOBTJSV8#+| ze+a^R_rYS@=|8nXT?P<6dzMfg3=?NiPU4Y$&&CKjc`v8-ewvVtr=aB!UZ|5FUNF!D z<&gv*Z9o20M3WcQfUrRVNA!N~^3vaaL0DkL7o92EVL(tzoHOjgG>lOf2W|W9|9v;V zbzwp_DFr=)aS8bjM)peeV&c>yYuOXIs=3p4O7&B#i&>W}HE^eUW?zNmxtH63EtG6W zKPIlltFXo<j&P?D%OJvyMM|%cqe@4egf<&1F;PGR0!FMbtx85qLM$Htwlv+uyP09u zVD03XGOyXrCMOigvYB{>&*2>GcN+4N*Z0qD4g~fAF!Czv2mwfG)^Y4nmhWfd_nZ?A zH2~V=Sm7^eQ_?@7wVgKuS_yO1s6GybtcAMW-Ze(;5Gz+!qt($iIAu{wG9yDGRnM~k zqn=2Uqr2;6;}ehlZcB2#n7)$@^j$}n9lt}p?rWp2Zod7d{HTAS7gRghRaLxyf^hV# z3CGb_4bh#HU^Kf%j!GdsXpJ3&Y*by$oUx$1scileHg)nMS?x7LybuVq1<#dchXTHQ z{;~NKL_@)t7X_1b@nFhIObkpmIi+@9g8`%!Fyb->^+hTyILZiZ8Wgf+5)=<h2K|KX zi>qx3I6sRSpE}WsXdsOd6ewCf`WiRh%2aK*oysS0XErB_?@sDBApu}&Ams$D(@bB< zP2+ZucgbRfqsrGg@6u}VnWVFif_B@@4UtBZqe35=uSz-xiu=}De{z1zLcus8XhX@X z7U>f2?MXOl&vi7|bo9pX{_|+@y<~j(NCTVM&T(sAX=Odt3(4pg*rY0<L|e<|wEL}1 z57Lk(IRf?JY;+Dn@d<~px-niFUcF)FypT867RN?*O0XivxKTRLgy1odeZFfU#<4*v z7jWeufQq8ULJO0)wg_-B>l<}#UQ4u8-qf#k(soF*Wo}+XGMB(iv^LmEm>LxpQ2Kt+ z!|pfutF$S7nj?pGvaDj_l%Hd9R-_Di>wd0)30LX+uKnnrjjb`t#X|ehflvH!h&G3` z!X*<(lZ*D59Fo%8PF<!PsE7?(PH6b}w6gg+42FISC%RILueQ87pf6D?7S4#rLDhpM zrYkhQx<46%zSuPaJ&jWAPg~h<&y!+!`DE=b-<lBF;I1sLF=#5gF9f7m|N4gV(+6aQ zL>C{nxy(o_XI%!m8CTQaMk;&+@zDVdof4C)5)o@&15_{tZINqJX`pM%A&P*TwO~#4 zspNAo*+_9!#`wQ{K<{T;I_SA55h(Su+a0YFY;lI!dKb(yDPQ<7a_Ds17%9N#{h9a( z1IXh5-x=wDgPa!Z6`j<h2oyN9q3NcMXbWVBdH3tocAEX4kWGTphu9vw`LL+E_3)@Q zulw<3_OFh6iaGH$%gH-ZxB)L#jb>^EjHi~CGylEqU+KY3jedg59)X<g>#vG0CF%D> zONn~qV5N{&VCQdIppV-vhSHy_t6HGGc!$xGjyE&*_TK5z(282}ZKvJ-RD%ZFrh_BX zrpL_lk^N{TN-CkNAUbO5$lH47sHm&tg6u#?e!N3WlMLkhVs8GU+XxD7l*oJ<^8MW% zK%RlRDqURECjU&Hfq+1@D2Dc{x0EPRH1xJ5A0(6~EEAdm5yT$MqCfRmbpR-c|G0}r zouN?VWF5{gFxIKsmgC7Idi7<8i6rUBXJ!rC|7p1AM=li#PQ&|bA?rx#<_2YNwYS)h zKKxyX`G^yWhw~U@zzz}}V&$^LJu>j+NVcmJYQd3J9}a)fx(n@s%lC8LJvt0ZcPU%g zWn>9LE-6X_8NaI2(<8n%_H&MyU+<33yL|c5kLS+G8Q$0TA`Y_;VwD~N3iC;NHD!=( zxKuWRAm=Nxab8EWm_0Er5YyAKmF?_Qv{LU7$EZQ%H$lHqV)~ATIBRiWq}>SYib2-D zK`H_HMRihpHOntm*wu&j0D(BV{8ych*gTJBic{|&>q3{&emXRjQWErJU~I%bNo_Hf z>F=lA8G?%ZIO?FL*BN%5?=jp`y7G`ftx%I)E>UuhPPHI%-}^#g4*xW^L0}8gBNO6f zXd@;T9O|~OQ+o^uKmZ{=I)@8H6DR34%@k{f{bGP2mFZL0DI*D0E&3{*cFxu7xJVtD z>f{fo`3kcUHlRD}4&)DrAaRj3y)k%3xV{B#ypO@eFSs&UxbfG&Rn$-96{o6g^a}7? z^W2-uWRfoGSJsPSDMQx8OjK3gg6qwf@4jj~*%Ma(i&7mBD_Pm1z67pR^Z?YKp&ngf zJ(Zt_0o^V{pqny;D)ciI6G@Z;sE|FAFJ24<Ek5q%A=KV<Amt#t<O$S61|-?sX%SJ- z#gV4n%#QWkk)m+2k5J6H0_}2?<K#{3KPVRIkefLQG%<+3<R(7;X{KbkDq$9>Z4pSO z(9M4%ZC{Kmc7W1iG)uHRTbubA<g4ra8bhMF&jft*Oi}&k`P$GE3_^(vF$|~ZQFm^v zA6JYF2@QJFGh>|sP-~0nj*aZtN|gWJ&=6E>dYq*cs3mrzL&h2JBjeD~Cm*MA;BF~S z#I(|q>hXEL`2kuS*@J%|Rz(HOT$RsT8r+U6;*3;Zg3Vx>(NjuIX{o(fZm)6#y5E0) zHxW5)q}eZa#C6+IB~jGY4s($m9ie5Z?F-ruQ^xgzVa}qxJkYsVbR)?22S@ZCSaqDB zi%oEdPQGF}SpXGfse@akKp8QBC=HZxxgO6E+iCilD)${=`G5y$1)rO<7s3#xi1t;9 zJr8$@M9n7i)3v*k6C?!L+)j_qg-toG*;5F+*KvPJxLU~gwF(V<7tN(dLX(g%{Ogna zpK_^#{bspnK)KxZq#(ajSe*e)l*iE_TiFvd&me4u;LBSlaIS&tsQ#GoAn18|j3xh4 zJs<8R6-<jUtf(|QX8o)|8tJRR3V<+vcs>6Umt3-{k}^%@1}GRnYvF~@hm>+saJB0g zdzoxE3BD~ZE6Lv1jYf)TIPOcPRq79uB{#F0^ct(ZdRj&;Aze%k<N)x7^8vmifWJVr z`lF+O=4`}0s5yiG4DvWPLmiu~*vv}iiY%D}xrD6af0Y=H_X3$fjJch`RlUg%_sF#Y zt2x#tu_?Rzfhzik6KO!ei{h`p+KW?GRU$Eg-M$pd?%4ny4PSpmn3wsHLR?P}qd*2^ zcq!Upy(IFSwt-8TSE&8{9<f5WK#W2!|Kf2JX;EZQXsBaO2^;OUv!RS;uiCaF9AE!k zmbFE4YwoEs<*2{p94Z(d{+~i2yUJRJ#_<%pD2gsB1mCI)!r;Ih%M{k^8EQ(y+q(HI z)D(1zrGT=e9mJR!yo?17u1XFRG}58DFsXtc4>RX@xhfe-v7Q5+<m6woks2tG=aA3H z^@h>Rg!mC!x}Nc?SwoOijn2h<fF%Cq#rx@txFVd_GbG33csTOl>B~w0o1DNet%F0L zQ<WC^C>WsixbZnt1bq_AiLN%1%dJURGtanYpS0sPjBA72B>9CH%hJDcWguC=@=jfO zV_8YBQS%ynHp7-$Ak1OPY$?k^R6J<KesKGnm8P{+IUVfUW7=5+Zd{E^pDmh7{n;+? z392gV=bV-_pgh^Rx%tGi@cSLB?a-<{VC`FobOw1hAVnyh+6Diffhb~c_Yeqj(2T!? zT3fWGtj5PhDfgpf1o-&Awm-e5J$I3O7uAq4_)9af_C@1|iiu?0)V<ivc99-tRC3vk zzokEnjmKg>^c%S-Z4Z_K`P=(j%__8+vFt3cqd7CN8o=yNE9#G-_AiG^S*%;TE-R~X zx)Ex+Ucy`1;(8a^^+rP1Cq`}nT8>1Zm$l1Kzws@BX-=H~Y~)?=r~q=Ju6nqMBMZ>o zTox&G(UdZZS>mZYY`mycRLlZLeg+%uem18m1ZXNc0%21}AA9tRj2akQ<3NE30A?uX z&3_=`=tDJxnJd%R<SDH!blz9kn}Blxqj{fJ1JrhbpNN`(<7a7NmxBuLJE)%5od)<O zGxs22|K+dUs~8SSg!>UssP9hP2b>7cwn5g`&lO$}7fD7_WEY4_f|TY#3YA{IT`z`a zfkW%_rEeGfi!9AgDy~YCmLQ5bIS@>;lAj)mf$wy;%8UwLG*mEOBH7ny)kIbaF6aIi zU{!t%g=x|%t{M1=ydlCBhr?+`at%b1n@7q_EdSe(@0&u~TkGKvpl5k5{Nw6W$9)1+ z6~Nsgtc-e_HqnMCK~0(BQud$mxYvIVWaS<((DHGUA@B1dP?u@=z^1Z7aG$0fKF@&m zbp(l5R{{po5NSu@=2ZHpO1Ur`5Chphmg{YfL^<lL2sWtCjv0QatfH2bc?ep0HWSLS z{7uD4UY#2Dz3?1qYp;`iU3qy5oq7^#suagEBGB^K+H4K-z6bzkVMgkK9zkQkG-!?S z(q_r-mcW%>sp5k_U_(k|3SGG>&7E441Hmcb##Onw$7lAh4)Thi8Hsd6D%kXyyuj&& z;{PB{PdOb32p>O=G<!~BV{Kvi<OMG4{Ug49nXg?s?qaa?4x04=13YJ^H^^RQxu)06 zDy~ME<Ilc$LCDrvxF88Gj(;X(x@zQ0NL5{M_4$%XJC_q1TT<)0(B94q{0pcKkVzW6 zbyh-Xqm5M9Z~9se-uQX~`x<4sF{VCDqiCIEoX3m9-(gd3JZ`c*x^#%Rj(dqD7kX?P zI!~&YpJQj!E{YxvK%eTOB>f2V8xw_%QMkZDb<*)No23PsE7tiHfQx}v&$}xDcPuNA zJj|`okdupchP%nV^RkWSKi?@YGaF7viuV5hq468AsubxcMKw)~bJvs2fGCU_mqi?z z@i~b!G1#L$3LA3=!O>|Q)V<ER<`%WSPFEGuhqE~pgHZJ0_Aqc|b*($n-$9u?6!pUs z537+}L5Uw=Au8{Qf%uUOqbpzk@C!E8%{`*Kz2L%L(`Moy;?QR<U$RRm@xtEz*tf#* zIgvx;)c?)~L2w~C9={ZQFVL)z4fLot$fN$be04rdQ>~dP94Lpd1Co5-<XBrsXILe3 zI}}7JQ%VHxR-w;~RfX5uVJ``}{4YoNp71j9TRA(}ObiM5_>6O!5{TGB<1S(sGy<$) zpEq3%As-+F(m(?=#@`?0y*1H4Cgo~061C4V!8)GH3xu2@Z^21t9WoAKXzYmB0Ik*6 zpdv{11=KUqL&iSmQHRftiM3|B%uIAft$qYsP3b=2!{sGUnP*RWnEWf5(p;LpWInaZ zt1wXhRi~y?qXaME$?0~_;?qD}1m9WBG9_0v^DAq-aDlwZVf)Sm)1L1SQ8Ftbk08&E zj%*|4Df}&uj2P#r<^HE^wp9w@JFBgAoAP;ORX60hEFqJrxSc5xP&j6zOg^?NTI3ZD zLgjIvBRn;6>&R}Fl)dRtmCH3c7&lU?<*x;Tfs%tmWQ*%7k%J8^K_bSlH$>ra>LnL$ z`#p#_35krz^1~_tuOvS?SV%?cwNjm47o{_H*RR}Gvv9MlzgUpYFw=le=tM!n(ef{$ zUZ7LNsSdfvqg><zX_3^{8d#LI!j}fUlH+OM)T^DK7f{je6Vp+HJ|fZpxg#(@9Dn9O zKgu=&9#dycUpd`Yk*ktUO}Yrgs-#+9{G!5E>L<Nw{PGAKLCK*qwBr@5Ux+`5;z%!z zNrVDb5?w7%euZJa3S9xSe3$?I#Kb!RCJ~gBlV#W1%aP?3q^%J^|MS$OKXhF;ya?}W z=BYia+g?ej-;dSZ)kIib@59#~(9?^`J@tncGvhOwq$Y)j);@Ms=Q-*>&r%)DW?fs& zF==jduO~NWc6du@>wJNRr`*vT+VLD$0>(MLv5$D7%LqVgdfvD-qen**Kn%n#)p}9l z&e%sdIO8ynVm~{swzX;syh=hI;2-t_qA|rWE<@Dz-EnZmti2YEaZ4lVy;<A*H*QN5 zt+j~jZIcA2?Ve~MuUPL?7r!L8wS=P?he2~!lRwpby1#%q6t*88A%grDKbe}gQ_Rk2 zNzu*v_ivP=G`6mbKk0E>#;xo25u%ZaDf3vJo@A)~n!WqYcj=aEcw$a=YvAg|$J9iM zNmTNS=x<d_0kyhP(k~zWotEGh1fIXgw_eBE+phVQ)#|xeL2;#KswV|<KQht)59i5y zjySNhUve)zxCEdVlllRV?xv?_k&Zds`fvZV?^m3>7)Mi5RHPJQ5*O!D|HnDs1N6-l zZ1<N+cjp7>W%pj!Jw38L)PQA!B!y%Wx-VRWeEEq%k750F49;z5E-h!131%PFhley2 zcoYGyTA-i5TfLD*Q)9MnPbaBLR>XU?H-^&q@A8o~^@9Rb-_UPfSe<r6ZKY7wBGI#; zXgkcJeF5(Gg98H-!+vZ1FiU>Y41*=yphf-^nCS<>jmj0bOZk$`^~7#G2q~kRT)-vN z?wy^oW`NrOX!B75%0n$<g-ajjZk9o!B@y4Qf!iHHj|9B1S1efR)g}}Hro_yhEb<GA zt{dxe<x>*l8Qk_XWcjn>9Cw>i*$edsv!s0r?9Yz}dw}Cg4~)Yq-Mq@eDO~XB;17?% zEJSM=Kb6+5v0n}^kMpi{EMrtQgn&=OXYFB;_{^BoVOcw1V8-q*(aSAIUKFhlXf+ta z+3ohjL)X(3;(Q8pv*|TM3!u7D{1Te42a~GJN<-Kt<(GL_Zpn1rUhV~k9<MQJQ%uge z*3a!Ph`fw3x;}y8<l7sc3!Fs@oKI;9ULBw$2=0=o6u8Wp;Qlk6zcrq@HZq>*OHi!V zt@FG-bvc@V+0=Nf^dKE{kY-iv-(0zlp1u2;$eR_$FHCUBPF2`LvVv-i9iAk9*T(kb zGEi&{pvq6U4n+Ye!q(t<!l2O#%8jphZw(h~lfZ{MvECY{!;vA5c=d<x@Ig1{+9u|( z@W^E#iHCNX0L*}oAOAEh5&*5Vn8VvUFrx1M;xeivfzivADg_<C>(zZ9R-)iH_viTD zl3YusQx)d4%Q}}3&jZNo;u#Wr18RpG)M(y08pXBd+OyqhT*R~GgvPG`eo23)`n;_@ zq5;^WaUr&5Jb5lQMs+kM|75Av(PDqZmq>`;Y*k~_+3D?(d0xoy>3GO$xIyEn1tw?t z&%Mg>g+7Ul;M%P#)zRZanY-WgAI026h~zsX0#;tn#c_*t>K&o4AI@n1Sf|tlsEy|D zC%?abe4?FtzRca)Z3<;=Qc0mx>=bZ)Hy|vF|M}g@)W;P65!M0n{llA?@`}(K(WHZd zZdaQ*RQ$`321QMPT-VbTyQ9$az@J>6k-)z^shre{!|V)m2rJ;Vch9lPG8aqCd~;v4 zlR<QX)r=mjUAr{*qwEcSSea*BmE1j)K{VmiWWgkE>wF>`LU`+Z@xcvqPtKhsSn}N( z{xfl1%}%#F?WIR@+}+)&O*dpsjXR()r;NY;U{g6ZbX~mj7K~|&irqvpkpY6mIXMZ` zyXM3GstLc)<@h>E3Nij+ubXzrNoX`wwfh-){{`ZF;QG@3$1#mf#NiIo<6=IOZk1vc zqstwCa20{wq+@)uR=)~+&t&V}OIXxat`u;%)9tF)OE_@M+WYJXef7yIChH#LQZY94 zJe~e%p2d>cHdzAyFz73|yo?_`!5?g6!@BS0b=>&9?+5-$!~59?z{!Vmr?@g5F_KPu zUGJWLyPOaUCxVl&2~>Zjt7o@d+B)E6EPlIj^>$@*d@osn#jKt4gB1!s`8%vN#t6Cf zzBsBBFV(A5r)*uJ*QCYJL7Ht#{-W>FXrM#h6hRl%6Ieyry+-iVBw$+gPAti3bA+<u z_9b8Rvw5p?fln?oPI6}3R#glyr@{E$C(IWf+$*$`OU!ET{Rp_M3G8vqyHz&ctCjj| z`@gZr3Gda(%IrF*CreNY{!|3bHqc$8&3(hFc3DV8{sO=2;?~Sq8wGz5yn9zW9G%iW zm7pt_srcG5siK%$+53d~U(v6r0H{Yy?RlIlmG`#+gJZj(J|VWnBsC2{HrjtuT|%;9 zU!7ZQq~%YzsjSLPxT&tnttDC(Hhb~qhO1T3e3u}R(pd0T8gb#(y}xrjiMSAhUh>vf zgxcy^f9~zB$LaaI?$#T1SPgJ5%}iW1UhJ$X4-=S_q!2;HU}E;}BCOB*2hp|(KS*)= z<6@ygo*F)VaajCa0mfcJik*#8h#L-1_v>pLiMJZEhg__TH}zLHyC&`azcNP3@}_`z zsq!~YJRUbC_eyl7e({$mY;rs#ULDCOyduQ-=dhMw+Wnj=DR+vrvAx|-sf5^|US&cu zcbEOkk-566bmh0Zx?d9B4n)m9mgEj3j-`Dh()*7{M=j&Xm-#<-uBEyd8AH#HS?}y^ zT3ZmePTZI`UMtsit*Rcs7#j{g?Oy&g?v8kGVJ~d+?sJOTFh@qr<+qd=$6KHdS_S4Y zkpSN&>#_TOM<JNqanio|AD#XB7GFL+e^M`#yQKERT=PGbv-gpaZ<+)RpmyyRi*O&g z-BaT|03npu>}(wM)m1gF@qQsB$~|c79j>FdHXx18wAEcNTDK{(mTJ;ZKxI_g@?dX0 z)qjpT1=aJa?vV~)oKUs}3S|o@*#fw2uOwA6#rd2=@eRi%6UMan{Vg&>aX9q+btcpD zK9Gn^z@3O5Ir=xpn-aGD=zQ_pU+s>x^G}^DnHub@OTNue)+PV>JN^UKIZQH>8xs<Z z&13L&5^7WG?zweK386Rti#VLQyYf4n-WP956$-SxJP(Vu3!gu4t`)R<<5titc=&O~ zL4awQX_8i2jC<1P#HKyybw^`|E$DIe;;3oAX2hDU?D+-w9X}NcZR=|0S4K#~ZX6Ed zt}k64q=w(6=yuwjm;o<=Ti!<wcbDr3VBn;1BMV(WyxF<7EZhIJoCsc(+YCx?)aClw zwMoyj<#$;oJLPr|kma*_3mM-q>?aO)U-ZAFS$1_#Cjpdf9)JnW0^Xi=Wn5hj#2vR! z8jJ3UU&<#mlT4zd;NRT-H6_;t0H@ekJB<66t35Ti_GjeHf$GLLPrL`qjQ^B>?&Y4d zNm@@W=f_{?>B#U4wLFR|BiF<q{KIjnbEVM`+MJb2sn{uY`RGnvZ|<vv81F?z_!<9w zeZlcx*MX7sOTZYq`-X@t-8T^&{O+x1PuKPEx=D7d#?JdzTLv6y4WKouEw1|pwfBoq zWcT&BnRY7$JJ-Btxy3=8w!P#A;2)087qJj*O;uW&0YjXZka*_z+#|U!K!tlaB^Y>Z z_`d_4vk1%ITX(=tkdUZ!lLr`XR!kZVr;7>)Dd2BBw+rK&MVbd00DCj~ms)j>@POCN zS-;qpV1Q*Bp+sHja)`85nG<iy0w_TLIbis(kPATs<IsIV8d09nHs4)s3+I-C+`Nl; z>KN%&`uIJtoOv_WADzmPTyhon{r6(6uc}8i*p4=rfMr|QSOLU5(ZtSwg+W4=a~2y& z>>Q6Y3(I>*NEqPC3%WO1HuFscsqMs#sIms5!y!nHA}{bju&bv@5b&s)@;U>U++Dt~ z-XFw_&PiWSx5h`AcjCXr@1M5BR-f33-5i1D2LezOX@IaXrQx6z-F{Hud_!W6v3@6= zT>0D82^2g#J-7R4i;HxEPcp|j2LOY5Hv=aZh8*}d@VViSuWoP1PLyq>#RK%kL{;7U ztI6gS&|ezwk$cf*@a6yzsJ0FWv3|-676F^m<Uc0LFTSt>dR{#(4)trBhQokPLOV|| zvfcwurmQ*R_!_Y!Ybo!3AXrd2rtW)iQ&M1nhjR=-$D8ND_JUvkGINB8z-NF9opKug z#lnv);Fw|$+&2-(E`F<LUS68-4Ioml0L#@Ny7@hh1mI$$zFiGy(><zMRye=_7b>A| zfs47D;-|uVP+<$Wi*Ny!pNna)Lw7I0ynBv-84`quwAq=E2=fJULSccYi#$tUd@gmM zL!Z3pklw%b3}DsO2=D)%-2=sNOM(h|SO$wx=Y}>WHgOws`ZD0Mt57b3KZ-+VtohhR zeZAp&31$?P%~QGU(w(sv!U>CIV8KYYGwZs|55%W8NQX^%a@B){Qvg2BGbP0q-}#Gy ze7CTr^_(%|H>xfAhJ*jH<3Cf>XlJ|*g;gW95B(m%-6+W~@o@#58#H=XtER1$viiE{ ziZ<et@SkJg$}j;}jC0b=tv|P<Q3y#PP-m!}W)9t0(E0}K1XjUn#r)K|1O63lMw*cM z2nBH?(%f*2-&#o+brYg=+YTXjR=<DbP!8UU+UO{AXgfvOPC?IDEk4zT<dep^zXzk! zqqz#?r%8sNy9BRyl+uTVnegvU%{SOEQ94kL*|0ffM)Kb9t0g;4XdHn)ORn2fhmSRJ z26)3haOz_wsUM~+O$KWPU@r=X10=4_puUnjO`31}=U}JjX4s<|2FmZ&bcW0W#2Lk$ zkk~{WHQ*M|{`i-1MKNAsK@Ww=cIX`ISLK8Sh)B%O)<b3kG2?<4xf~y#ym(d*D_r&Z zg+ajzpw@&7#(tR3K0O~@o|&+Um$4HznyuB{c^P3aTi3(<nDAkvNjyvk#Ko;Ed!_Hv z2rOA{VNiu3-W<=5%hph#V+KBEJAiP>Ti4R6AF672R6}DB{@~mLG|94Hbb)yRi5g<o zQp{HMpvmgU*bnmLV)PjN0BFt7T=3ICI?3tV5IFWRbNo!VUSF0}@uoZ*{`(QG9ZWut zjnA8vMIRi~e~$1>_XM0f@)a5G2q|8b4wl}ENqMVDpY6aX$;B>vohoX`Gtb4GiI#@- zflU@~LdrvyuRi%`Dc^u)?o>wbIc%{l5CO2L_!6LOOsnp6F(S<r1FlRe>smU%3e+OF z2977>)!=?^WN5Dvi_cLSI-|U>vJ}cTAaAk@knCkV<^j;h%r=aSYwESW9B>1M?|ZZL z+gEtVR_NZOHh|hwAa1vR{vCV=j!-8vYOS`18m^RxC5904$z%Y!x6Iuhax!i21;?uh z=x|ydq5<pMOH<N++~Ar>yfxq4T=ceADgzT~eZ%s?NU79*RZ1r=-F);Qco!+n4p8AJ zD3Nw+`6dV$qoj^T@B|ZM6*iBvogv`KQM?_GeE)Sez@8~e!X3qV;?W-x{$pls(Y!B@ zfMJBpq0|^hcu_Ltm0%vdT}<ZLz-D%%^Zyq&gubI0DMqVXV`ln2`JcNWU<WlzH4rZJ zuOYPok|BSw*e;{zRDj_LL&2*M9<sf!2{0eJec;iA@I^)UCC__e6WeRXYFVI+&ruRu zKx>OWu;Q`Ss=!$`fwis6T-^zbs>Bx2RLJ!KD>V)5et{L*@gbmq$4vzCCsH_#pT|r^ zRKM`PW=$8wMe20GBrw2P$7Ls;USkp~r^}ha$#eV<+PrD9lj;UIIWa!bK3$)6n$$__ z>+3_3-9>=l5xr2O{6hpmKq*gxkhj;ykO<~Dt6lT$8Tx<tcoRe{>PXl!km{ln1Cx(5 zkG)!|=+smaKWlo9=Fm{ooEd*4Jvl;&-Z5rR$N3-S3U(OM@J!*D88rhN=^p^3=G4l~ zUf&qnSr7@FR+^-Rg0X9iPScjWj;X7(pL+E9v;q%R?*1<dyoPlQpup9HPWFw3YD$>@ zrSV}#5B=M}`UZH{*QI5^_HFAnHwl!par~_D2pEyaePZ5l{3gIgZ-C{7mI=&Ca79H$ z^EO;IhJ>4U!Ipc^?s;+Wk$^A$lpibs71bu|E!HyLcs>{F4PZ=-M(lO5sL8npE~#lq z@kJZ(O0!_Cyy(Iy5_^n_idxr=U3AYpZXght5zZ_1l4s?W&Sn+wh1=YqJrGluo%(x7 z>}O$ilNZ0sS+q&MiM<mjLOUbjK6?1C@U-(%&U>e{YXxbPeO~GsQwOXiP$d6_5^zWb zUhD(LlupzNkejKwRJ;CUeUY(t0KTKm^WIO1HKE|pE3f?89-UWKVt)?Y#?o~cFV2|i zHqt-7;e(ZheoZQMo0M!V^#||$5&v2m()3N70_DkzA%6(+1Mp_+D?fOreBgKl{(l5; zlq>e<M|%>Yyf|*=aD3b#o_T)iB60jKc|{}V#h=w>#>r;trzI@cg!$U8>d$_4Tp$Ug zI_)LwV5~%KpM^g09Eg@SXcv@IsO|dd!J=F{0xip4An2h)nsLke04vsR5ZTF)5}$#8 zK><LDt+ejE;1fRHkZ-}04N(!ZW~4Ogi)T4Paw8R6g17rj$y0vu?Q#DD%kf7B(r*pv z-Tbi83j2&)EOZDb?~rRCU70-yX+fTh-Sc_9|6!x!LX1z|Trt|l><ti7u7GqZ3e+4` zkoKy?F@M5#!4ykT2+<wbg<Ef<0z|<eS4FA9C~g=y7l=do%l7*3Z|Cnf6WMDd`hnS4 zIO;@YuGwpuUL}IQXjrJee7Gu(iq41FxnikYFB&?HF5gNokmQo=+6pTGC%tJ$h0jE$ zz<TmQ$)0SNFzfrjr^aE=e_q8&Fw3;$K6VvIv%3MizFP!SAQNK=I9KZQB&sNDECdC= zpVDpk>`%`D0-@E%141VG3#;sEEGFuLSAA3DuD=$6W9uOTIAWL_t@KQ}9e_`<C3R1Y z`=1Hx3t90;A)iEx&}21&4a-~)4R^mw#GVqW`^3G(n<K%Lj}J;SE=}a2vu9%+*kH_9 z9isz@->W=*naGy3#WLAceY+uDvgf(R@xHH7KI1paAl<X3UIQ;l&A=-SdbD$&oMh+3 zGhPL_A(J#JY@;uMO<deP2%P{YUP9EDPk+sHp*J=-T7d-EpFMp`n}y=j!JN=0rt$re zut+V)5P1W`{2>bL%(pE>xgd-Gk0Grl!5>SnkxrMvR*dPOCH8GYJMAW8*}v=+RylHV z1B}m^4&fW&i9J%x_Fcr|`)Gl&N&j#8sZ`)dSM*<Te0<7pT|p;X9G~QNdD*p6V(v!l z<XyG>ZVyrH3W;w^jh>E0zE8e&`#(gTbySpHxb{`L1?d!qMr!Cr&;jX`E&-A5F6kP& z8>B;!QX1)Q6s5bRL(*^0`<=7SzwdfoGd#1Oz3+Y9*M0qB2DUN6dV*O<H6JB26p(9_ zSLTAc$o~T#(JBJ#rW9yq>Hgj58o`k3SjT65wS+Ux^e}aH3EqW@UDEDb_!7vehkb)Z zlUw>sWDZ)@A;Ku%eo9LN=j*R8<bO4_42n9qkv`QZN>ij!jx|d&Q)}rvhPZ%TWf<-e z3Xr7L9Y4nyhu#eJ0gi?S5tc%K9uT&s8XZl4gALks1C;^LKk4&DQM%cXg6&m0cNieG zeZjK_s=uG7tGts4HI-)}WTq^*vAYY81b+oK5%W?>T8}Iq^^)XH!zXUsG(3>bi1}k0 zjM)DM;n_iL8AxLG&lnXZR&M}kc&Eig1fic9WIZhv+>Jk&EC^#IOpBO=^tVRJlJ4eP zoPnI~WDs}#rp4_KA~rn&cPVbkYoDDRjhlx`z%DDaq`0_zm)`lyvD|Akluh)0?$`o3 zl9dWNhlkDq>`llSW2uyC&W>?DyucKLE8l!36q@UVf5zNu-eC1ZZWF9BG6uqrvaAKh z*zse(*BL~AsZ9Er$7Cb02_&9Bb4f(8!fcU?jcwk2`R03nG25W_Nx=W+e0T3A<;|G* z9(Vqy)lp^{aQ;Lw$-K*K0hfWDQZ(sp4Ylj*YYM*ppvX*&^v0|WT~QhcheqIZvEuYl zyeXIaUvO$)=y)=+syv*Ic)&X|iO1#5Bd(tnD-#8@ar4hDJX2O}G_L$WPzwHJjN+v8 zm4Xjk%BBEeKYjW%)+tGXcZ7vWF!aq>l5Y~9%X_Y&01{z|6Ec+GCjBJ&B<mWf^nM>< zm_Z{AE{k?k)+x^Gz}hri;vsoqI2T4Q7IgtpPTS?2JOU!yhHFj%`3h*y@1OHwLw3t* zFj2qlGTv|MkS*^TmhqL_dnB1cxKT`bq&bNb=`0()ogtx9Cn~)IkJ>!r-voZ<z(kkp z1viHxC-R%~yT?Ri$uyZ^%k3lp!PkCQBt?&%h{Ec~s<@S?QpU3;bwoQ9qUBX;TsBj- z;A>CEXqfybWPOeqG1XL^fw6L#YAEU4_JMQ|4MIAi(;b{NsW9}M{2~dXUB6J&j5iG$ z;}jX~6dpq^%*Wi%9OgjSX>XPkWjL8f$zJg+$iYbbbl$zx;RR%z866Hgrc8<&5M>sF zix<pHtOhMO0~%BMG-fx~P>Co?ftoYdCrxrz=gRp_@%z^|u4MFy<=;gy!5&CE5!CM> zLX@ioRe7X>o^~WzO%sW3DC73a1ntl3ZWoN+$7Cvl(3f#%r60@UglHH-mf^x&x&cB@ z+N^&)nf_HGwJ8elkuk3)zt_p@5V=%Ngd1xFV^Z<3h>n|?o9}k{7@9z_AvHiTP@dVR zexFR?q#X68SnJ77VP320x&U>>3L-E)-h}*q#9gveQpi{%1s}~%9aZhYBh6!s8QGjY zapc6SUzEEl3d?CoI|;4o_3u}|F_R*aqWzSz0bg_mSfeK<V$bO`Ygfm8!VMv{Ssu^W zNCVR~WXO0sm#x>iQ6RX_BTDIilYT9!?a{-MzLqRP*9aU5TM;zV$T`29tn0UD)hEPd zm1P~5{T843EGN4UC0UqAFi|uNo9rDRs?rN4Nw;}jOyR>pxS9&$l$TWVRN-YlQ=#(_ z0#s%bml+k*sozqb+Fta5s>Vsgc|53_L6d0y;Iax!F2L*yo$dUtKj`f>L>@%JEWz{% zDkp6oV=m!i#Fu#bm;&IL``(AaP$6UpuAf&zCt*~?5ijNrEib2fXnfj4X+M4Bq&8vn zWPuDCK{q(aZ^E;7sE}>x0RZ6#YFp?p(e?89H*4?@=Lf&lwXWVfm4Md>1%j;L)0f`^ zNb1b@w=I~X07F|Uo`%$=OSTkZOJsr>s`|%xkL$z4bI-ez{tJPeIt0E*Q<7CA9mMZv zU~R<<HX7r;60Jrv1H@Dfh3w2-4t}^*aMSZfcQZST%keaYRq}+&2oVFug862$Q2c*) z%m40ktIlJEHh{u}53bhWTZ95qJRj!Id1Hc`mCm3B+9!=6lp=065xp2V-Qk=LFQfEy z-#$p(>=bN*IvIx^{1ezG6SizJK1|VOu1^8Ca|kMe!FH`IE!!ua+Rh#o;@xf$xGJtk zIk?yLp|+TJ2Ui{6uq=iuVsv4fPIDvZ-hpB6C4lnq0-X;C67N2hR|`DTyn6wj6<>nw z*WWOpB8wa7q`FFSCB7>%f<eb5{`nZlAk8Md(3Kr8RegiV7T7xy)-dPT#V()&w>lc3 z`}1j#Q#v+D)C$Tpcuy}OVAKX0-2y)1&byCyz<;3UivMEE>Pzi)$G>}f108ojaZ3eY zyE^U3aFGfLFPvtaxW}F0Pv$=o7@+^HxU6+;!}?oU^}{wTxSu8vKjQJXp+^^WVp3ko z^UqeRw0sl<CLhBIK-@IjZMz9;0vYVh4z>q?2mG+?juu!dUH%Fl)9F*&Y@H>?;1*v| zaTKRYyn3pqH61ZdRqA%$-Bd;k96JqFwqLlp(7L(SLco0>q_(b3+IqGM=Rc|rI6(6i zgR{Euy+ExbP2+p6I>17a4<cP9c%8RKRw~JrE+<s9u5fis1Mj(YXm^EqlRk?e2xAO0 zt{*(AMRhJGs`k4symV;&o1iSd3cD8Jr|ohW=_ZxqKpY^kn#c?As{rn0->2YK5s$5* zVv$lB#a6D@A6%<{Jy`A|r(%)15W6N^w14B2YrbBCj}56Zej=FIHrZ_7v`OqJ7n0>P z#Nq@JUH9mG1r{8F5OQ>L4(h{YdJ8DW?RQ#nN?b}aM>5`|oL?N4h`6OxJ!5>s-+`oF z+_?lVvoIRW;8KHGk4vGE{XI~Le@Rue{yaW^9M^@GLb``X!(|yWcaZBR+H8Ge_IvzM zVs>5Re6aN1NY=Y<p~mjBN>}5u?&4+_4tw1`Gk4N(brJd#&DuwZ4YBJiV#hi+@j3w) zu0h1eD`Ip)3E9HwFK5TyCu(aY(UVsV_u^o)+XN+Z3ZU8VXHXz83a!9Ri*RhLMDh`Z zzPNXI4{{$~h6O+chwn$p_022ns|o^d-!k*S6DGJHftz2-XIoHg5fFgiSku+|Vs;WN zUks#Z($;BF1j})RXMCr$7wF@xzzC7W3ITT0H~`<R@E(!pY}4>P#RCp?1k(K)V1{R$ zf1r@)KL@&JM2wHIzypF10(wd>fKrwW_-T8-Dk_b{QBS-uF){g97h;diY^rFM=u%E; zL?pcwN5C82CO0}?ipkn&9|G;xM$0MC);OA!p-E<|YLuFqo<;&?3al2f82|5!%7Bcx z85=-pj7j)vJ750f1Q@Ib)3{AHKtOcjqT9TpjJTSbnud;K`u7mV0&->1lVAk><rD~C zLkM^Y6+TF{e0Hu2h{Nd%fCKLYgm}4JabFF`>zn;**Yn_zC)~q(v(>qGK(8!c`O6iP z3vB*Nx5wRhvpBCAf#Vz&OU*8?wSlZ`h)U%p-%$lI3(vLH?229hi5J$NuzlScv1zgx zAYPihj=*{HKkK3Ms#lhlB4-r=;{z<8(0KY+dKPar7j>ogKB#lSgTXiie{b|XxUuSr zspTselRSV;YY<fa90<HRB|t~if*VIU2HKgBwzVn9A`;#^0ZL_AD(eH#W@Dj#cOII8 z0DGsb$*iIxezOD7B;^{u0mP60pvQnNs3H!?UIyuu&M#t2yn*R%<_|~z*5+pl44qtD zsKXuUF0|CtqqJ^ab~jZ_pDUMABrg?yT&yY<Hk?+8KNV#H&vRf|j?&;w@?VZLhpDM4 zp@e2y61wRtDra7P{@nKN|5g-ymc>p)BbtOo4*<?7eNX`cPfeXkAz%BLlao`K<Qk!8 z4>?A(cwJas1H{pcPh@-o5Y6?Hv6E2D03s)qXG~B!IR@P1^~O!rgdx-gu;>IZu4v%o z<aFNZf4^@e{A3DX&DwHJdf1sssh9>=R0ieX@sjOm?NO?D7m33+Rr*Jy;4p(cehCQa zre`V|8iko??k$<vM}1Y5w$B=qN5{wU0HF%<3+sr2kMI;WTdDGP*46V}cGNp`eY(A` zW40z3?gIgAuLhfoMoc?dCh{RMf^_G(|FUQt6=vhSlwX`+KeW~!Q54gqn`ltAA0Uu| zrFnVJlQJ_aIHs;e0Ir7pTj~oPf}!DIEFxm!J);%vZmzEX*yoy05<P+P``#6A<Oneu zp*Y2*x<CJ|dTxC@gHOJ@Rg1PDV*)?y<&?RCY$#9G_ccXnVfjlBeX6<-rZQx|IYnBB zyoG8liaJ~<e{)hAS|JCiO18F`1P=I=0#cOyy=u(V_|r|_*;KN=8{@ShStn3bZvtKp zaS8fPDevmWIFQb)Nhu(+1ElGF0sUame5U9Wbngp+biFsx%|h5aI|sH}bI@krb7}(6 z94|#!K3T8bY*lt4xFax0$83x9*0nk#DZJHYPKmc6jy89}<5{ROXeq}IqYsToM=(QS z$&x5OKrOGxTmdki(%+;iogh^v)d`d#g8-eCpLoEHFO6?vOR)8o>ng;P3*_hgbcQP` zq(|Qy1B{h6z8fNrr!@{avJ=_oODejaJ8t9#Lb%6aT9EDlTsNZSy2|^3TR9ZyG*<$_ zDX}>&uBa%l#fJma!$`VgkQn_2V8qV_@)Yv)^By@+%1NrK#<OC4Oz6B5X+?EAT+l(x zlST$<(W#K`Fn5Z&fvZUG5ZaZrWg4I}*#O&e`L8d^f>(voA!nk@6#95F5O%$<Gtj3p zbPHkD>bP{$tB+z{Vh95$(+pLMNe>d(MMLrV2uU<1+JBX*m&CmNqA~TN5EcTLS7Y^M zY*cr&0~*@i8>}Op^QTV=NIiDn`4Vh3A^;)=*wgch;{rHnUQbSjLQYLIxQX}<F9a}O z(c=#Vn;v}Bos#d9WfndEvUEW@*yZ^Ihv8?A5ZKcMDpfRGj5N7e{TLzKo%7V@DAE2y zRQy@?BLK=EBgdqiLrF>27fZUb$qBGMGUy*x3N$9Zl&p>oJjU@OL>&ToLno>D;b5aN zs8$r}qZB2-W5!y@U1zCwK<+|E#YDwKjzk`M9ErU&<?y^~2X&iAt&=Cu$AekAGZwDm zrp6ybDWpb3cSy5~nyrd+*zF^(Uwvj6od3~5)SrdT9V7v-fV$8RaCiAJ&c9AbOPeUa z+%@P@W3E-smtR%L|MO2ReA;+ZA5$eyZH2s@sUL)%TCm!%8b*@+B+u{aMGhG920Dv6 z$@jELl_p@xxz_bG1dB*FJwfXOJr`My=KLU%#!Tf*OkfuuyHStq>*)Kb+Nn~{1X*Hv z0UX?sOu6i!ZRRPQ-auGlvHI()7uFI9IAV$TR}%xpKHs??E%kpC4vxo6LLWlMTb79S zg}uCvURxP{0%ItZA;o7HnPoV6g!@XxPlZcGSF{JoF$f9;(Aj9VrFg;om<=&9A(Jx* zO<FOv|J_OLdW=Z<RQCx`O~IWYBA-@SItG|PkT746-vW^__7aRt2&!Uvhen)421jY> zS4}x<kYn;2VK|6YDVivcV-F!kE3!T@TL=neibVAn>KkrE?Z7B-3U6f`Le0cGJI2MP zvIl5UU9w4@F`g-k*ZK0aaJp_DuSQ&zV>$tTi47h?<s^LAi&EL@rY&;UD|s(dr(X2F z-xj%_`xE_uUhu7$L2P}4bIV;H$n<k7D?U?GQ_e^lzZyCDmh*#Obn&vxvyivF>U&D< zsE~~(J^`a$rm_rEE7kB&#wlHCCmMu%1PF)NC!J=BLfsCuJx>Rj_~6m@IZl~g0n08> zE^^qoC<LSZ<iNjfOtrSN^=Y-SO21mHX;tse2{_iu3q&NThC~Ts3Sv<99EF`oG4k!` zIm$AltqSLcphHd_T>??3n)mmLow6#-F0#7h_PkpGUEzb)m2HwiF%8c@z@7EY_>BeA z4d0VH5-Ob+9%lwd1x5}9SUci|h*BgYwM}V|^WN-)<R5qCB+DUBlY~Y)UJSMEMkB-$ z*XVH2rqiAc-I*y%%$QN}m|_EAK!OeH#m5_-r7j;{onjLfQ~x{AfQfQOVxzFMiBXPw zigKO~7}Bz`EQRE_ygk_xQ&IEKlCY9mm47+f&RH0(h_xF+_QWzM0tbs0ms(W&Pnl&N zS4Yr~UxOWmY`fj8lN%Qxt6q{#`QYL9>Zp1*^pjQMkbF&NpP$)g-56n6z2!{veN6cy z?2HMmuZPgrZyS%!%1UxDGhv?m9?H!BzdYw78q#mKXdeevfDxiobpM{EgvI(={JM(8 zpDf;ZBWgLRBWXJgLN;0BuqyT0C_vC$j8XU<aI3yIONyA8ZGRT~_kA|=GQLc9#9(>+ zFBLbuUCP5R>Cnndmn>e7>_KaAU4uROE{sjMBv}B*M`iq_=D`*5GnW5vS5ROWY9ozs z@zX1nQl8)NHyPO6M_{yQF1%oJ%R5x{xnK-C0VYSSx|!f?vC2ASm2__>w<r>nrqAdy zzQ&P#OuDbeX60!ADq!8yK8cl;Ckfw}Ta-Md2PKk-i0HlWN^YX}F-1{m^s*uCV@!i2 z`XsEk7sWE+9_vla!A;_kQA7io#8dMnL?_W0ouI{CB8Rps%gGc)(heZfUN43q#X@OD zHP7FH?nwg)p*@VhxyEwkwi`DqUa6fzm^h6YP<5Gc;XY@dYrn67aZK8Ox6A+PeVB|? zNWPeSbp~yZGM&O|9&~|n;yu_@pi{ip=y)t7+s6WOCM}37WWE84l8Q}w;>RNl5jGIg zngX2bm#gV!24Pha<n!E8hb-b%ax#>SKbd#+Of1IxOfg}U0c+o3CX%)J$Y1^;kmv=Z z%;+D^r+8ikksm#4KZcd<0779-3t)NgZ0M0&4`Pc6F~5UXfgy8Fa{m{8+w0FrHD6ku zv`JESd-iZ&e(vG<T6J*S_#PQ`bL5v#o&tikUT(uf0D7DjgP<Q<;5I5bH?i_^u+XWf zCP;vd(Suy`V@08^8zntf&?12gl@*8iGfG(2ZsE&(m1+}QufY>?8L^%Z^q44&&cz$E zlpkcc`lE%Tn`3ZL;-LiYt%$CFhWcdH_o(FPjec1(NPFvYXTAwt@<--HmuE|Cm4UAk zG!HOEjoZAPs>`p2EHGg>C<30~pq)a+7sE@7xC6E5-b0Lu3g(Bd8a<1ifE*h8z90!( zOcw=z6;^n3kX)i5lOie3OBPd&@En@f&|?bqI@vQeSmYPi-fpz_ZwqI+`zZ^CWJMK) z;TC_z4Sg>(|C4vtu`u}xR2G|E)rBJ0fX-i$IJEntyD~N&S3Y+r=$fAGhdQoFy9anh zESCfbGC>qnX_r!l>BB3kSN)h<RdBkgTi@NH)QpIClrLrGz=&b-AG|rLkuao;$TZQZ zc4H1k!^hD>PpJdqaQH7DhkWW7sCyaIQBL%vswDLa6X>xRIMM1hL+R&3_T-h=Mrw}J zf<^9GhR9w|x%y3VX%)A))2y5Kg6g~OBD+Z4BR`g*pGoBS-R!R6<4r}nyfX`mh$v)f zlji-v-2&xjXAWRqT*A$o#LXhf(_gVgd5zQ?cy4@%Dkl=ju$->StwB2ykni3s6H9#x zZdtl`u1|oXQ9v}33HiM1@iLy2%lKo8y_ol<!qDJs1o{PWHt)##tKZ*|lf7d2(f6si zZ@fEENJ`0S@KNF+*gBp1U!jE9TQcxHiu~{$Zq1$ImU5yLC(w_}$V*)Tz@3TfXV{8U zD(1m~ePuLC#rZ+mtYTZi-1Q1C?RwC#c}&N)wi6K{Fnf;&=bNWIdXvI(K-k)wc`)2K zbnsIE2`-xd-LyTxX@K6J5^sVFwkt_y1{H#QY@8DNQ$Nj16@76Hu3x+{q?;|k_rVv- z6KFwC?877-iamK0TpK;c31mqs*s$Y@2|d4yPpvj<wpFFxtND<4lNT&$@<Fo_bF6)# zc`s?wB$BNTc$5n|L@7pE(3zz%SQ0-d7SbJ!+ttrj1I5Y)RfYV6%G9sIkHvZKbEzLc z(3NcCU1goWpDuk__Qf!-r(3xkGP+A!!rd1vvV~?`t&m~5)wh6)c?jB7I}W@B&wsMD z?&hr(uJ-6tc&-0c+HFE2lw^!FbqX<n60y9u_iI<ImFDEfL_O)Fi-3=iMLfol1>YTQ ztBiXaPhBd>9>(S%4QEM;_n8oq^BgSg`(o1Vf}A!9Ot?a?#st@~vzOm<ILxYcsjHg> ziJuB!c9pi9dpMVS2`pxIM-g_<_c4NL6O?=~ZV{5>wO2L)bbzOOAO~=O1vek0uY@5C z-v~2~G)UmU0ar%FSZ5k5hvIkzK?cYM;phYyFOmjjrrrQcdWM8<gH@4dFH6>qd#u<c zx-Y1{GqeQzf7Le*@$h;auT1dI+Y~Gxf*uK_6|f%the=0R;ySDyhCV$g!m_%GPENsX zJOn5@L^og^N`W?Nz)ZLWD4d^p0q<Vq@bi*a!3g8W1UO<zGC%{>LE{q-V-kZ7pvB}r z`)RAgLI2$QL#cI2B7P20FhOyD)93%F^5pAd7w_C9KYW<2%Fjn9Kp&Fa`vN+Jd>cth zpIexj^uF0qd4a?wY2bgtj!|%v_0LVEI}O>QIDPxZ^W?41^Xthlt=Hm_t%k3bejb+( zmVnTQki609XOY*>u!2l7<&Ky##0m(~3|7W-b7&-3=DFm8+SuAQ;WozGcc5Re572F5 zq#4Cw=yk}a`0t(1$;mQNTO&@g-#tikia8g`qWyFw6LiR{>JRWl=g1~=lq*(^Mod#P zjjnfP{75DH&AmoJ>}Z;l+&UJ{0wGk`Dq89~U~gI&cn7PsQ#-s7n}8G<)r@)#zG>=x z&yjs-Fbf$KVqpBqNDcSk{&$=p&oLR#`SVK`D^e<uR#o1T^5wF0qYmsWbqn~3hnAD| z!CuCWf$KdLpwT93P`IEten2$fo1}F4JiMe6DfThuw6mu2#UjBTfKBDG5|9r7ozb~s zR|`D6ph6w>2aY^<hx(c_4`8-(Id=qo)_kDe@B(H%l_DBUV5e7^7=i2(OKNRVud45# z4>tnV#4>X*|H3Y<l#lCrv?S&N_{*qMW(@lF$p^o2F3So6n^_dh>>a*XhBO>z9poEG z8zh!afi<SN1I!)huKPdPH2|Ji1sn!a;KDf_?@_?GKBuJ<?<wM@(D@6Ul)wkL3iV%3 zH#EjOf?%l%HIw%*;)Ai4U2lS-stJZ(71pY#c`|b(^_wM&mb_Zc(KpUOSj0XU7C3Gv zM*~6hgI{63v5gndU#OZ_0<r4z70I#5p!DZI_3T3cH2CWTh*=3NAV>r^1s#flBu@mR z_o-D6B<(%5&vB=2s7k7V@5PU~e3M2mYcYmTbSMxtG98&l=NH$)y`oJ`P5HNnEu1bx zEG4C%I^BTy73>Gd&@1Q=RZ!8Rs=NU0>{=B$xyVc791tjW^BBO>EC5aYjT}ik1#9VN zCT89D<2h7b;J-?PORm8O@aS=iN=m{2SCvE<oZJmh1^C5AUU~rWMGCa-&~0mai8CzH z?Onl(8FqSh7F0lh1RM0I{&(B)ZyKL3=><d_F$`F|33g~wIYmXBZ19P~{lmw>`=6Xd z`s8NxOm00nVP(u!1?SpY77UIhY1_$KhLfr|b4o7Zs&3>FQ7oj%E4?H}*i=9#%m>Ck zYe--Y^#~mhQUW3HT1S+I3uId-2U5u(dk~>8XPo!6zi=;<!tRS`Gnsa?i{%bb0o67G zPxFDEmk&IJZ07)(91P|^h$)FWj4|aPGnpYEK$#PSbN3;#*36N18-ZIaY{}<Lkqttp zozXI5TWx7jSlmWtV?i=2FYn*MQ$HTyPCTpU)r!T&|HrkKMqfMaU?Lk$Zs)-LzP;n2 z`5uJY;$)e*9Q;yc3SRcw|BNw<ZyF4K0Zj3`W9dIseW6cpXAn$OZ?;iWxq5Boi7n)6 zYM=+V#TeUwMIijCX4cRfVX@?Hh~nd?O2PmsLW!3%zF?d)AJ{1j#7-z3d+_rS4Em~J zIN1I4Wd@j$fISdq9oI*jru6{2#`y>#aX2K5+DlgK5gEg^<)Lyu_4omS@H`7qwb1Ll zejYKGv&fg42hOo(@7H=FbUEG`4t+p9n<v1WW7~8|NqIiE&MH-n&-@KP(_SXoJ9cVy z-+(F3(u0huWitdVEl~9_K#<@rf~t1U31@Q+2)aKF*MPZm1>ICS7;8px_L*4{UNnYR zI*rfqOHJN^e!>kSpOcU+ThnRjBTD}M4fBQh2Ovp0YWZ;m+=7{=$tH8#`L8%Ql!6mO z-vvV^6|>@0a@#<m{IXP)SYp&yaOU#hYreq~^XHW=!9*bdyDpUvB;>He)-xq4TbWhG z((9m2zCHP8e;kQafAZ0F54FfdI0y&IvtW(*;t0FQyI`XwxwO{-Pe#(?XZ1BWX7`xn z=3-vw==VT24VWp#UX^m4K1U{->>lU^Luxb1p1Qgj4}3@OGjqDSx+XyVjSmcv*)B`O z`!KF2H=vmTG9TXueJcg}l4VZQ&ZnpuJBjpJ06NpQz(7N04!2>3_l2HB29%ToraHp7 z6;oUAVa1YlX`;`40>u9`qU+gKN?@*Z`*gR$*N3p@ADOd0#oyYPWG20<nh-85bd6x8 zA+jXFRp*imfcb*+%4*GRZ{`ia+t8EN)RlP!Dh!s2zIgQJVaaKjy`g`nX*&mtzOT4P zVCbY!>UUR{IWsjJ`a{EmUx9!-;++y6^$`3o@)`f>B52Y*L)fiA1A-4tq1fx{@UwGk z+7Y=xl3iiyMT^F|J`urk8s~cBySn3WeB^tBN{`C<^9+V6e92$U6jhvI1gnNMWyUeq zCnuD8eHU`z0*0KHIPquz1W!-_Yp_*?l?IL>_p!!ByQ)wcjl=iHXgk4XcZ-=<X`@I) zOwQ0!Qc|kV5nQzQz;p96lv(`1Q%Rj;jYQO+8*@Bdl+i0xRXtxlsC7sdcGuW}D=;#< zKf+n9iWgsd>c^(v5{TL%Gp=WJ&8!y@kq37e$@ytudhdNZnSq0C$XslU%oXgjlOMK6 z-<N6EJlIN3ih*Ysa2yFUdAVfa(*5ha6?ltjg}Lm2LN)<%z6fqWVTknRB4`&IEBKSf zIdB0hzCPhH>tRZR)xO2e!5d{L#m56EZ)>t1W=`tAYk>okA>i5|$G<eJ!g}t$!EH0i zN?Jq|j^10M&{WXs5ZkWBaveGrk@wX*ppXeD#=x<`>qyjwuneyeV!x4>$0`J!%X+&- z-3M@?8v^nbdzA?bB2@`ceVOCPGcmP1hY;j<K$1(ya$oXv8s(b}uh2lUyjUevXadsV zCg^m<ft(UKJG&}`w7!-!0k-)ZW<aISmkt|3bTGg_E1?94<A5G6D-J4QmpDW!2{ET( z<>m%ZPm$i>6mL5S?=SMhYGLq%nwQ7IeM2908_Z<44~`vwOZT!6Y{oHEolxt?+O}dJ ztOWMs#_1lC7kzuYzx*Pv<lL1!bSgj5B|ZY`Dfi`QFo*tGvs+3`=b_hmt8S<p`+6%g zNF`VYn|VAUObFp?3=XC}`=sLYcQp(w)^7vgxMM^}$UbIP-M?fJg&vMRISugn7V*CV z&6X!+HDw=`TATPEA!3$5ftJnc1wt4w3Ms}bb?P=|)RmJ{4ZT><StwY)fPlcasMw^> z5w92m{|+GP?7+j70{oaNY+MEJ&%l!%XumzIxX_|^$wHU~Yr!5r433o-%RXn(l&&+X zP?N6U74+`X^a)&qFKPqKe*RQi&KOBDSu9Mk-&9UN$T%_2Th)PT^P!ZmEr`et-C>HH zr}!Dj48)ndjPe`e+I?#+scpjY_MMzvxA$JWIoDfQ^bTvbrnltpwC6#G(K9d*Ax{e_ zwC-5?q+Eol;86Xp)_f|MV~XYOS8~6Yv6{mOcYi5U(zKF@V%savWB3~D<OIU~B|V_v zC$^pCJfa>Mukk{smfi()q$?s;bS$!gLpzfoOJ+qX4PyqpbzvE(8YoGomCP{_ooU>g zcxI-kkz^Vm80?%3NW%q(QRz08G3;w$Kvv#;<+Fv0=*0ELAc3l!#3`~eqj5DMrAY!L z*i>*ld?4kc`sviAk=OvWy8M*08u6RA<oC}}#p6{I=TEX{r7yHNsx#>gP6h4f2*vp^ z&zPB~>#goR2^bXX=@=8yUU_w6+MB0IX?DG01&9V#rGuMr-USXXl(jf%T#SYflTunK zX7BKG3LHj(!2|UyE)9LI*>!&c2>F33rs`e6R>ZH&qN#?mqiuiHrO_B_@9@6#j0m=N z#`761@W>128qDou*)K>EUkb-lVQvTSvkLG8@h+bD$dV5AfLp;cuk%S6zAR7D%5!_t zlSferQz!CE(YYAQFnTbe|ER4Vp%d`uRdqagX|nF@ihyq1jDX-078u1MqqHR*><8lz z1vv4DiW8lipAQj~Jc&fS*1fJ=iCDpVUZh1Pe~(wm`OSxtCjOqRyVkEE2#=~bgim2F z91^|3f(_wl+DKS4w1@bmbD2ne*v@wUdEN$Ej(if=IkJ0A!MZ&ovY4d4rbYI!E%@I8 z;(v`#m@OUTLgy-)BL&bgg4l0>o#HuA?2{0om13ue!2=NP_Ed|d;`997^%(;L!)vDg zW>|nyAj08q2Ik6yxiR##FPp+DCd>imSWTxthRS@Z@C~c!8o}3}pf9C$Ydw|;Z1oT3 zf$Yu_jSs*Y*Q@#T6xhDxN|dtY2EJ4;fqtUvyTkwTib*(T+(A<|1fj*7BT5h#&~jk1 zYsQqZt=JyRZpndPfi&Cr_IXgfem;sG<+xRFamOKU6F52r;~M{YCqc4U;>d`?VjLnw zg|*wx2~10R#jp{c8^rs9%Cxr@0ecsV=m0YyyPk*@EG{svTJe@$r{-OVV2HAL;bX^_ z8F7gITLQoB+>VF?HFP#Fp{q-3aBy(a#Y}A-(M~yu`Yd7@AO^LR&+;U@&FaoVn)(Y` zZ|c7h1>NkbVUN)G1rQ&HL7VmS?%;hYV6l&@>-IS(KD448-GH{-u1MUnhtXQpl?=b< zso8H((rCi`o{Q;sE9)Z}b%*mj5L^`+czHGVeYOrRhevb{gtvgjYUOooSV*%SpVJeo zjCXp9aOFWy*TD6)2w2p-3{DB5TS&m;GYDq2IDvPdW6Scex5c*RwO;Bh@viqX_g0GQ zfbG@m!LU}5>xpfF7>HPe{NWq@6Q3S#$JgS<hJ?_=ULFs!>oAvfDxkkBC!bLEKpbB; z)2<5*j1PF=9uD83o>Duu0@<FpAL7`1=1gfa5H3TbwL)%69_!;i;G-YU@8f^Bl?m$; z81jvfKE-w%`8L9HeHP(=O6xdsV%TT2mg``%;raf-&}H)V&0=CTq(gD-<5Bi$pvPu& z5B_xsZ{uEh1#gx2`A+`VE2aE6kn{N}H^B2fXZznzTmH}S{w)JjOP@J#rSdL%3Ty#9 zXM}$a9@)5*&*R;Jwx;4#{Ba*9ANfMD?RZWw$W{1^;z-)-0Q9+UD0|p;<vnHnRx=D? zLa*D8muW0F&3kAQC$9VMz6HXMQX99gJ@MKx)=<(pO$T$6tP4~B#l*$uY#6oEPNnm* zv#)zCB%aH_3>m3d@H}x(iFRpN3P#qiS#o79azdH`b`x|seCWmratpbkRnj^6F|_yb z*FN3%5f*D<r@^8Mt)~Vi)qdcHvv*I|2|Dd0hNa8IN77l>K106Y&km&*oY<#AdDlJs z62FQvdG>6d47kwY43hXr4>0F?zMYEj85N6j^qq?EptpTf0{Zxkjp?7w2`x$djwr7b zc5kSA9(!iDj@HGYy}{~YMjJ4l;ck^!Mmh=<RfGA_<20dJDz|!)?e?}f<C^n$2Hz<e z$X+YV#kGr`)tV1$Z0$2nB{fI5Zx~$LdAM^zjGhUI@<8_(jY{G#H~iOr4ww4N8m)bF z*zl?Fk)LrN2)8(VeWSAVG<Sfx?iUko1XxYgCw_&H+5FZ`oi_LtR_Y(U4|k6|qkLoy zFU&<ev+AX`&N5Qt`y&K$(tTbVZgQRsd!LG_cAR1-O%KS*<fNWIr*4HqKZ^E>FVh{# zE{OpUR*_MM-_0GBLHo^Quf)aUT}6;YB-(#j<)e}A9s2svHx7TfFkTg>QHtBr0mWno z{K{;HKOz>O`vT~EcE4FQ<}HWyY+62tHXKdR(wAB0!`O90$@%T^%d=C8#exqZxJ`J{ z+b+lRzWTzfQsek;RfpXwdTv+v6(hdCSe-+nbNaW$``(c;VjA`}4eXe_+QMvjm(KQR zHnAzTP8)Vqn0j&tUW*(uvhjWM1Bf$93AI)V_N@%(`hA?EzZuA^&xc!Vxm%`J*hHE4 z;6b%M?7D`Fd_6VGih_e;PYhZW2KQL`9q(vlCJGssZawvuh5~%+?CO2Iqc11KuMIA^ z-4r(|y#oSIpgDuXves*PbY!cBusJWXqhaS)L;4{a#qib*u!<eK#5|SFT7y#YQ^7d& zhM(v&3dec7C<nh^cJrSd1xV&TDekLyS;>_u*<RTkx);Jtr`4>iNl!LesqYnAooxL~ zyYR15Ev}$B=^YQEW~~-F(45iXJ09=!aeEta37MR2^9R8#{gYXm;jObAiE26>vn);e zGtZyHcSijGu>G8g>K+){eA-`7xwluY3Rn&pb<~WzQ<1?kWDcFH{89ZSPVuiZmLIa) z;QNjSYm5Easn_<8(_>9J&z4D!*4F!nJy1`XpfnPOFzPjt4Y6x}Fi(wfwBBLTm33O` zF9bfveSduYVIj)<rtu7#TjKFHC!Nb*5j<*N{oAu}CbJ$q$ETh-vC>8{$MYo(<I_HM zK4J0DT>CiH;^P*VoVDxwZ|mk8I7^w!Mc>~3`G(EMu()vX^>Bt|LpM18vvBCY_szE| zSE{qNziUio_X>lv2nbFn4-Hw$^5+iy*!4oD{W5L4T+O7JDhyg;5}RtntsB3979H## zzuiKQSOFX7f>;LH?tG^~>iKG$ho9d!hYM>jT`xz`r&>ME23M(9w0_gm)4YEMya&S7 zr>L7^EiQd;Jk!T3hz6PK2A}nO8+R9{N#yk8FzXS5=4uTp51`A$jBIOiu6v0`x5010 zfH*ipTs>e*j?z%0WqdS4nBfa~@Y<SDZkxwnq@?0+8}{7~&Ktsn<NVv;9t;m2G@KBF zc>F{2!f&b9e^>Q$Q=5zgC4%>T#?F@C7+l$0WHws3o!Wj~7QZ{tv!#+7tfVO4?#hfa zJS6XhUitpqnmhDV24Y&A`c}}}yc!|EbH%P-tpGKBj?Z6{-`_NyuPu7z(!V-2fFO*b z`m#)x>yX!KPj%VSmDD*MY7+K_wtTJx+TOCiDO1;b(Elpsr_-u{EB@Dbce*6@@`Hn- z*SiI@d@=2F%Es8&4ZdB!t7W&u)qMHAh|oFZ`^2wU*&*<=kNa=w*9G)=w`<0?#NB-D zyo~?;IRdHuo&$}jCIf<@-VVO}X7SsvhyF&xmsgIq&20|sW&6bbj@BhHtW*BOH6Og= z`+eoOdbGU`$I;;h|KqQ}qxbiG_jO6_Xsd>Od~I;whm@3>@gp$Cx@|_&=x>&BZB4M1 zn@mkB`lhIOHZlA1fYFs_U;fVw;o$hF<DXdUDn{!?o}cA^rfLQjCB)jaQ`TZKdDiW( z*Gn*jpGe`6&Tgms_#Dq_n$1z{vOe`i&2g26w)1E?T8&dJKZDX}Wr45>{UXgfL&L4= zT~;1nE&i3c$sr#~sf~Mn`m^x??z^pwR-~hT@m-nmW1!E~qaA)k!kt)pxs-afMViyw zAp)L;=fm7NGa?kC^I5pd(UO6885zD)v{D-r1z2f<%l+~je1p$|&ngFYTsJ+s!ksyt zqjCN;TtXjioPZUNbatz~hYjjw-b4&t3!|RXHvfLHyLt(YG4H5{TdZU^;$8=BuakqP z*YJ0kyJ%b*b_anN#Kq(G(45FNJ1Uy>grwqYJ&4Xq!sTFT%JxgWe6pH-Vm=Qm_8Xo( zoff&i#MHi)+D6U1)^zy|9QJ2M?D4!;)J=eU*KN>&&Sa8tv`BL$Quo$YXJhHeziwOp z)=f|L?Z0VWj<fLCq~lwBytN0T{i^h>_Uk}Cl!S}~#*ivemGpvv%@CI>!)p|dqej!8 zjb~Y1&;1p~QnrmerDIRd1vgYIwL1b@&&EohzuGu(9&;Z5m+A9w@msU9em$1g6qf$N zRE6SompA0hakYcx*fDa!-`{X+IsymmL@R<zLB4(v2xlA#Wc&;|HJ-bj+iR4#tRJ;3 zE+#(=rl_~6<G}(jB)-lnGA|kjW7^5k!|^`*L%kIES=_aK&u}Bht;$X7DR_em%s5-m z6M`26+&)^D{BU+OnBQFI<3K8IsZ7~Eb)<6dNSZs$;gBe9=He{Sc<|WL*V&qLeeqKA zx0Q_{$Q62i_HY|YvALU%H^Aq8KtJEy<^Iwxr@q=qnY^&6tH*qTj)ba=!<po?2H9VI zVHuUfwBvqUq3J$sc|+pgb*#Q)FHxr^4#Y99roNY0Pxij=v)OlGZ%8EmY!I^APW729 zYv@mVY@B+RbyMiTTtn5nJV(L-=>E^tqjn!7>K4<u1()?`r^Sm|>!2{qFu7p4ppJIc zZ8jn!OI!Ge0BIY1`~2!+d-5r|#6RJ5343YiA&R+yvoy3T|D5l4iPRS77AiAC8bt%p z8-`hzqPJJc<7YQKIDyw5BTeYbRwoFKU<2A>t|)LC_iowuU~J#sI=0nT>Ew(ESiOtC z?)YxMd|b*(X40-IMS5k?0fjygC>z2=9eg~&`x6hH>fG*?B^y`tsDxI@x5C)gIU^PS z)s3gj!eWvNK&nNM+x#HaemlwOH}5dQZ|ah|jz*_(Sdc9CX#?w}J$<43pz+N-K#@kH zX@qqQS1Wlx4{Cu3PEhSCJM~AYzvxLu!-U2T(T!P8sEGTy*Hg}IgCadvY_ooPJb`gE zV@TZlXdSnVeZAwZi-pcwBj8|i-B_poL(Sd!G+qYrpSgxqn)ZKhLl?a24EHLk+oyc& z==swWsJs^(p=Ysgpq2f8Q#GncI_q<m^}{|Q(MOLMOrviV^+tz(<=jE^40HMQ#IK!N z4;r>}IyIw5UxO4ZjRTT@`$nf@ARMRTYKJbY!5)I5gjD>c=on>4id{nf-Zm+P^++L; zA*kb8$5y|_oge-xsBaHkl-MuGza`1r`@G6%u-Rw*>wqLjQ9oCUK4A}87}&IL^TOF| zIa%N6q(B?^{B40wK#&0`<Tc*KcIu5D6?A+n?lrY~4!ncc&ysn{qwposs3-MUfn&s| zKsF6CIsCcU-Ep^7rec$fG%tYClPG-kG`kBaPssP+a!Pbv4LJoj)+vh$_KmqVH#fYe zexb(#6plG9s9IAcF8>M}Lm$=_M1|Z&n4G#Ie8FsUF1`-cL+CKQPmOU2VUf8~=U?NQ zd8e5Zi-8*7*wnCpB2qxL**g^ts~XNVTrH0dT)QmG+%K6;(So(JAdN}L$jE^UC>xju zP<AkZsOBq(fM0B5_GEo?Qo31gn-PbhpQXjDV(nn_!HPNtecj$SRr%rmLiu#{;QACm zxaGf9ZF1UL*pMe!G1z+*?$9b!7|2hlTeZM-nJX4Ux8#*^^-Q=TYnGNJ?MNfA#q#2D z;_0q@jR`dGzLh#GzD==FY_!m!K*J>;w$cAq+3|z+Z2o_rAp9JFOQ26#*p56nO-*|~ zI+I-+n<W!<>4?TEJ!Q<A(l(9S$^d887RVMUH@A_Tuc%SiCrdBUN)@&-Xd|vV^J#l4 zwZ-4J@wQUGmn%Pbq3_CI>n#QugbLt)t?Ul!(S6%N(7|wrXSp^B@{y$97S-wEx9>(n zsXwl*&2s^Cez>|>m)dKG_BZe;dc1~o(sFJzeb#Kle&c!_io5FYaB(e|Wh5VVk{TZq zYFVZM6J||OIHo$e*AxKg2o=xu`^PvQI!okbf5KeZ-YCincK=GMbVc!8&3N)hvl6=@ z>l8Olkt~x07NuMNfwnsgFBO1Y^u=UX$zuB`ve{rx-vu&BMx@g>PlIDLG)_erzs*!= z2`upGYc`^;B@_+QGw>;b>fov6&#cKP*Uey7sNFPf%p*;;g-|t+hN?g&;#S{!eku=j zKWqicV>~UVak)4flk!{Q;89os)5e@f1AVW#FZseCvNOxQe*GSO86mcfdJB!i#~EeC z<gN7G-uJ4>MYSOfCOyvx*G9@@$I+F2_}9?LZIYZDTka9ykN><JM%;lW^k_bHI23%v zpAtT2CFB*$1iT;@%z6Ls4h8FiToX2_YdMjZqXd&lH+J%<ceQMu_5i^;TGyBR)#m5k z1wVe2#s4+F85HgKO13LZuA0tI;fYm*9ERk=!SoD%Hxmq%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 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
* 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 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
* [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 zcmY(q1z1$y8#Rg|AV`RGgCm^|NQcybbcfR2jWkF~4Bai#Aqa?sbazQA-Jx{n-Q(~7 z-TU3=QOB7%bIv~dt+m$so-ic^DJ%?93?w8ZtT)mUDo9Ak#7IaFRUe~*Pa+b(`hk}R z&MH!3Nae%i+u+S3b5VIwB&5pdCpU&D;61v7w6-%65_Tuz?}J2UY%(OI`}{W&qH6AX ze=^Yw)ViiV;Ka!6%YS{yWVHVwT=z9D+51Y=UF09lR08j_1OB3)mCt-d`YbFkBk=i| zgy%kOWYF!VfET@u*fVZf>e>0@ZCbC*Ietzzv*E#1&Kfc7mzW(K|Nay`v_kX4`rjK4 zZc^3%J_i35LFL;1&zszS4wV0M44kP0AI`wYzz}0yUJtXHEK%RkC{&=BH;T>A%j*n= z+o}#&O_w+IncSSV+-^jFfx{Oo`@L^qA<0RJ3*5EvO_9?H)xZpU;t)gP5M{o>2(<+7 z+tcZ)`@3t|AMgL>8TUl}dL39}J-xFCizI#H>Dj=d_4V~Rq*!_??+sq!P&yC$V{NIQ z1@h|LZhLdoCKSEn<Kz8;724HiacOC9*P*0YrcKBr0XEC+!J@tV?jodBvCt@1CMFe< z!#ejvBdkrHs#n%i78!?LeQ~s1&;=fLc16>#-j`0d5iAghxRaC9RJ>zkgX?a8e<E|f zXdo(SxBY947GEEsB;ya(NF#%XvdL_TgM)Hd14BcTEuG4%-I1r6b?gT^PZ?J|E*ssc z46^H<`|?FKiVGrb&oz2DrwVy}7be=gZ0r&W^)ty|o{bqwd&T@%8?B0v^=&Vlgfk$< zm3q~e(|j0Hto@)&-}f?~++|esXuB{QH3zXxhQwpW7_bDn3|?ihF1pPuuX4eIR%Fp4 z!xw}MuQK^v<21g!?YlT!4OIGPCivD%ykdr&<P5#PH#axbogB|_M8rB7!vCEL#*z|% zOPf+PdeIJ>=XgeNbiv+oqhd2<`kzI#<=R5y+S|o!U0%i+5{Jn&d7kFE?Jw}ZHUIZL z?vtsvQP1!(3rc4a2q3YBk5+%|H#27$*!^F>{%-rbKT2S)C_KdQ1=I|~0`XOMw*k0= zCDfDJMPJqP&mplz2BbB&XTQ?}{#(=_Vo@^%)ocN&(j;M+!<ZW*+4giQ|L(pM&Pc2r z@LR#t?aQ+13+S37;cG6Qn*Y8E2VaSe*olhzr$!N@V;!^m-d&nIt6C$j3Hx_axji#Z zo*LM5)ml4UzN@X=A^9)=F8R{<%Q{1j)bx*%S8$2o#2vqzLn(?%&VOMMilWN#`4LY| zrhr?NK2MEm$eY@?$R;*le~hV}p32~P+{(LF$lqc+KE`mdvn!9BAVDk!mvbsS^x52m zLI&^H+yp5))}_~vzu&9tVgKJv#HFSdpKTwuCK%3t^Y5Lqoxvx9YM{E1zrZsII{Dm_ zm33{0Z1S2X3jGY?bu=)CqhF!%#SUsCQ@o4vG1w8>-i^(0h`B4G`Z~;dI_|+3CVc{L z;l%B<*A#W*Lm1XUPja)!{)#EVJpEDQ!8J~V`{Dy*^oTv}5;D15LN*<Xh4KIHjs)CY z(3^^ry(#zKF4G5pMT#Qe=)o@%gw#w%o67rstA_ko)TJE=#&PqCLWfA8{xH7rtq)2J zV>9NRvNdk#{d5<<zB4|}?5`>aJH?`^7#UUOS0E*?9N}!kCz<k_uc`j{J`q<uq<%D< z;Zkr`oL;8F?k=M$%T;{Z`EYcaDx1L3@7!kIv#&E})?e=VUUkkUB~9S-1vjG#<vqex zQ-hDMbpmO!d90@f={(5(+uH&I33Q<b2BH(`X$yXrIJR<sfp9|akN(hR9hB#?R~lv7 zL8(w}y7v@Jn%Bl&*vX?8{wj{|>Ps<C>9>v(7P1-QYhEpgqDJBLIk4}{D>f*kyqH~{ zuVOqEq(H?^Dno~ua!p5UYeQCYj5l4+v!QQc3cYb{ddyq1Zu36e7jv=u0!panDBHMb zO9f{iseF}^2mjfXxDw&m9QEli&7tg@U#cNL@fMjm7Q^~CiXK+c_XRC&78sTBD63SL zVtrjWc1al44a`=*D@*x+GNMsRWK2O@G~wKXp7MawdGICo{~ZhrM2@QkNm*Wl&SJ(d ztUHTZ$r)i=lQ_N_LWA-}wBr0|X3|#gYaUE_W!a>zy*vMR&fuX>?dd$uC4<h2v3+=f z*^WqdR@xRLz#IQ<A~V>;KEii4xI}hVuFx8)npP8rn^Gg06xRBU<0f38qh319IIpIp z&++N9ihrVTeVSd(q-powQRZiv_kX2<ezq3b9-=Kd(iS*L^3|2<eJa8!aP*eUdk-C2 zbn&30Ktnk-cM?e{MWaIR3xxCk+Mdq9^k1eP19_JpKALQG>KXpb%tU)r=(dkN(&m7* zVT?^q(*zx1v~#F<-{XvJrmo3(?rH9(V8%)^=?1qJ_PV^|7xjKlr>W5W>Ia;JO=zG_ zFCTq_-ktM>-(onrqSo}L<ED#=nAf3TL>yEu`9xb?Lp#O%C9DB+hgiot?_>bGyryl_ z?CLru^!Td3i1+Y$Vp0;_Z=34KiDFgy$NyEsBJvtB7j06qwP}67#B$vq{d}7l5FDWs zrKfMJFFY1ToG$AI*1yCrj^D282`Sv$McsCjM4s0AUrs#T&Y+;u%ay4%>cW_{t)0)4 zPv?gE-ds>m{41akPee*x^t;z>aNB1q%zU3Fc+?9KwHWGPCOK%0#o1aMs?>)hVh*PD zr+=7tEi$nm?NDRstPSw@nD9rVWH{ZOP971yW_kSTzv!hz<R%p3``hzm&Jn?C7&GyQ zxCcLK7Ub{k_Ix0|+EpfD<$<Dl*&K#}oJRkC!ZC%Y*_Z3d`rpP}7d$p6weT1_X0)`> zR`cQ&;?@U}2R5_)D1-hviJ?Ta{87|1QoXtZE}ubZ6NjFg-}Wt&?)H}d>^Y%>xS98V zfAuK{{58R<aR1DtvlB(}^b9rs9YOu(m$t7)LdP6dcL8z#9j;bJfJ<9#J~HJ&{AyPI zi*nbePbX;%;?;q{XWgMEJKS<Bt-oysmkQpOe)+Bv-2dN(5hAW}a^f=ojH;0uE2a`x z=zKmz-qH529qkznwCr1%n2SkUHeRK$MA$*-Sim?0H}?8wkNY*M%kk!9+yQ^!p3p=f z{;ql_?@^4<8#)s#y-lnVbN#+FjyF|-^AbS>jmd1fC6Iqv+wgw@?9`S-j>SIBKsCuT zXPrHS;Z2dhn%9PVeQp)l)X{pl%rUQJV>iDuwT@?Z;YNa&!BY@{xieDqy6@mm4qh%5 zYYK;$$=4RWBsg)q$Fk(P){&iD(S(r~?O>J+Gac>;Ia|&$u6Yy%Eu`UnQdFDW^8ibH zy*w2am4K)JmF#{)kVZafy@=NJ)FknA9sRmD`lg|*Rz#UD+2T?$)^J|ynkHPmx==1D zO!s4WO9uCfcJya8_mBR8wASs?Bov7uv9vz7r~L1e%E?Ggm$JIT#~N5wlC`J`kxofz zv;%p%ev!OF%OLSSpLeS=8%hJH0O1?CPKd(GK+M%(bF_N}rx;bH!-{e;<Qt3%$fng| zZ_IVsBfTlJn|M*0+?#^MNOPzN0KwW;ZuEED|6I$c`|W^oTzWb)A0J;^*0LdGcg*nY z7*8XK;bY$<gQf$U)NjR89uZR#7`)$CrMHtaFZxlI3JA?nH&}-dD}ZagM1jaSsQ%8E zCE}zA>wMLmn*LI_Bqst%t_+cle2f<qXV?gYO=q5xjMZeHY>ej*R&_57to@Coev8ML zCEp}1$;Tg$^c1I5<4aX6|G(V?i^5fgnh%qwa#_ZuM&WZxhG3C^{H56!`vLm#!E<!Q z=BycJUOoEvoNl=$?meQqCYa8g0nX)9^&5t>Ma^;y(E->^`TEl{P15otLgS23yzqUp z1)~#b0{WPi-?jD?`&R#hL;p(}Ab^qpoKa+}TYBiRmGdz`sY9AYqg;m(K!EgbcJpt$ zqp7I=-w2CD6tf>JVrIL@Hb}2L!v7pb&ph*vZIEdFU#hOGM3{xEamJ?A9NC4KVKtBs zYuC1mP7x;G_5VA>ebTU0wNT-y5pfurot4-5p6%t_zimxHZ0n%>msm^=r$ewh8D(W< za1<gcmH9w?Jp&-=1#(JNYWmxMTgHuQEh&L+XLWzEij#h0^@1w}W?R3W1TZye(7#_9 zs$nA%Pgj7RZX*o|>~p)!LfJXgenHR!WFpg@iDJ^b4jhqQG%9aueV^0B!E6!zj=Q^S z`}zB`-}a`nRi-<JSjQz5Eq8C5FE_|sJ)T4mvW^eI?h$ST;rr^!@!4dhGpqp{c7J!f z9xFwOE`|lEG{}o`<p9v7=-TJ{EYa`&dNFNZnUeICRi1TQhWAllTw)?^JiX%7Sk!8i zfC5|n+B3fUn`6`d1jdq>MepOGs@?J*{ed{bMcB`@TBCO>ySwWgS6#ln5E9aj;@@HD z8yrj+%atk((}<^&Ukimq#h_x5Zq>~6xR(FJh==|Ncv)e0zBj*~psElnMdicw_!Yd< zXsS$m9Fz`MD<rOyipC=ub#k-ejABC&8!D!s^;>+aJWs8#1a1yG){h3*HB&cNBG{Au ze*djdUcZsH@@AERp5ckxyle6vveO1nPUq|S2G{lcWWDj&IQndFmABt*7;S%j8#C8x zbg%S2n>FW8tLwlP=uI`xDjp^xrm<XTtk3!n;hq{Hz(>GXjkTujqgj~+*yp(H?22$? z2wor>&Qyp<?o%q?iyQ=liWYq^Ygw52Ek`kss=CeRd|s36V!!#ioCz;Q_OR)j)uc=} zoCG49#4_frJRX~yr<l$yAAQ|&e=U-j{r;C_*AwmLvtQPpBe)RQmhh*9n~T+GsMDJC zytxUo<fD9xi-YBoAdYsY>%X-FLFjlV%?$KClwM`z4y_NY?z&Dnq0kfW|NIOKf#kT? zQ>1l0xAO}Oh|!-M)4gBxaho-YU{&*<QFF*9VAik*r25IY;c#=cThSE(-K*@OWV#1* zXrh#WP6K$@S0=1zp|g~m(`o(j@3!wePA4_h(M8*k2Fn+GuWer~w$DXOo+}Gx`Q3TU z`(CaSsdIRq{b8Ke^ZF9i@9-C}F6rB=Q`@LSgpt)AAW+et+3Ff#X%;N<4rvLC31iQ1 zz+C;w`o1JVQ{-Bf-rM8(R)oPvADr5yABQ7jploQuHRhZKSozZeil>JK_E~D@->2*N zoKEsT3{VNIa+)<w?vv-4e=(5EKJD@1dcURRJ>?Au1Wz9wRc9Z39GFO5_CFUR>qeC3 zy&wB(^~4+o9|VEV{Nr_l#amNf(iOhm0oMr_y+DpuXCO*3&LQRqy8&mLR!+UY-S^|K zeH0wv@qBi6mOs30?LB^!u+P=j3)kVMm2l<>mN<LLF6(67_pwnF0y|si=NxX$i14oz zMp&SLbS4#tluLTAelrUIm1j6~W}I#T9++X+xs=)7M`2NKGdy>S`8_fe9>G`~CcTz} ziFS&5Tm6wE=KrXma`fGX(Pt?gGjA0CshQT}zyD`jOpqWp&KLbOQH=MS&NW6}9om)3 zS0$ff3kxLBGw(Ms$8~X5Haj<V69`=@3Y{i2MBkqLR16$l3#=zek$k^5r%hkA=(gx{ zG$L|eB<9qC#nU@eX*}6Mz#(?Dcz>t)eZkZE^5%GC5{n%|SGV>mA8}P_7$Td=<$e8I zKoqa1Kx&kKxbe)Cl>r6`Dq7H~3eQ3zdB3&bYVjf^#9^q)MDA1Ykx9m86OyD#EW9qL z%A^^0xISN$kfb@sc((rs+KAGW0B%&Hpti^52Zx3Fvbx0>_(kD)BUeU;Mi%_|07*Sf z%amc9<&$|?VAz)XczeW@*M3u>4C=yq3Q>DTG?AXWsGj#>*OG;@X<*+LV3i*yN;MUw z__f$O2TKqIPof7{-0+AwB5;f&k48%)7jvIW!dx5b85Em1`Fd^+ijT*zFG#v#`sxE8 zj<$B>PPhq|5%{XLLK-HROhEZ$T{ptv`w>1$Rz;b6i8QPu6ZQgYWZVJ!o%%psw*HUj zaayK>FAZ#;<Qka`rsPmoI{Y38#lh$X`oN^np5NUu72npU!0>5|wv3%2o<%jj2*MR? z;t`iYHv|xdOSI!7UYH*4t1?i)wLY0N-W&}y*rDNVM2p<H>xRbgQ8Lg1gqvs_#RuDT zdHJ@pF`2?`ZjA5=7x>)@K&Rk^n}+3i?_&lG6i6Tgv3r%#+Sb<&?4NwPWhx6_4aL>v zv>0N#9%yG~C@ascT`5Z;>==sHj(Pz6vm53=U7T*;tUcWq#$_sAY4DI2;}K=C)&b6R z-QbA(dZMP9hXH|$fzjp7<?$pd+nz(}zGE8-(U#Tv2}<SGCqz_DxY#@b=gh@0c*uj- zrc6hiZ{REWjU&y!up!&;PMheYvn(83UoEB{TBb3)g&IE)%efi&iVv(DGndW?<KHq` zdr>0JDnIj5=4!>*8@D;nro1#ZnJ7{&(aLaJ@Nfcox%A;fpnm*-QwqKHy*<@*RcgH@ zVf?p=X|FTWNq+Kx^BD#3J^NXxIKAkeX1w-LwV*qa%vy_ODc=TU%ISJyxYpP2sFW=! zkB23kG$_nX0x!uxiENNd<9Z|iCFR9GS!FUZ(g56)&7;BE8MAa-7hO!!JCK>`8*)%I zkVJNha!=gU+Za%f`8I|#OU<;O5V1`-%q>9s{WqBsvi@ZRCwyGQ7SCj)AQ)yPE=Te8 zq^5qNqVT0eVkDh>T5m9sUJ>N!U|-!{!>$^m0e--jl2*v1eakH!02k`VEHDK<&uv;= z^$e8fFH=2N!syk;THz5j@`w#Hb~gCmhIxmk+cmtoI{V8k{t;W<bhWgiS%n144@QEt zj!cV$f^=S;9>AztP=r=up5*}}O7gziE_5yL?xFH4a~8L&TSncU`tr`<8^VG%r2pi{ zP+X0DVIUZ;1G6dT`|~{oJ735|)_Q~PzWb&2WWNoKiUpL+Dol{c&UsMaHi%N^$0?d| zeV9?k-*Ud-wVM&i=e$w0jZaz87@>pMg(4=KxLTkN!8xV_Jc!_OjX-+j_w?6(W<b~# z`oK)3%ytZ5QQJYk;~}1s4^N{!2sRD*)zz_X`CWIm$g#K+C^rlb$TaKF$Q?eEKOeqz z^clcd`LWkf`pxnu9q;dd?*CsD80CR|r~+kYdmoV&0R1Z``^0;5d%k!y@3y#29y-AG zLr(JmMj|?Ez(pe>qM!JL@aHOfzl{3NPol_tK;oO6{EI6^p2HlGd&8pFs3oPxk!eJI z4SqZsi<M=pn(BbyFn#_&GXq=pP^)7F6wKso->c%fwy5G~SQqc?>Lr`+uB;Q6hm#H> z*czm#$7Y_wy=~x6bY34C5*#A9AE>Krx7X*uqb?=0k5i1JVg>f<#1rWedxa3f5aEoA z`P2_Mywj*CTJa^mowXxvL}9V20-vN5pM&v$C(+<~E4=u53PThhUijm18TD2Z2fAR9 z%e8o@Vf&*~;Vuizew<u#O?@BN#Q6bmMrrJS=A8)|b?-^XFOX&+xG|f@tdQe~a{&Cr z8bYaAOW>beVxH}zL3Xc>wV@(%a}a1#GpWI^xRz#Ju8ddib2cjlC@9yfLnv4v+~S`? zZOqxh@Mxicc&K%)FI~vC_XIt;&KKWAH2|F;mwj+RtI4y5i5UL1@nR*y>9C7v_XhV} z>j+p51I~Z9$vN0!;DZT!bs!O}Nk7$7^mxN^X(Q>)irlyvmx*e5aoc0r_wT>|gB<|< zkqEkf1?nwKNQJ^<Eq3RDwee;Ea9!fCFh<17<fHJ1601V}>@z)Sq;PkRG{?U+(>o>0 z>BxiClpbrislq-DCT3<OQf0H%=CZl4NE3i*l)4bx&e8fW2a{sk*`?OD38ITEr;TAY ziWstWHDYggB4P9tq$NadPkc4(aQO=dM7hb&%BtjKcUrOh#~%OoT)neH9tfh?!3pd; z1gW1y%E}>*EqYAnziQjhjLkrvqJ6qGCN5v7B}$%LZF6%5fv69?|7}Z;BYg1*+@%jT zkUcUV-+@v)Fu>lz!f7@bv*>%x%KHmpI*tEwE2$wiDqd0QyS7Jm8>SKg58Jm;3Y5u_ z5wO<N^EoC7oyH9+oq4s>MaYJW8VV4%6M(Ry3xw+YTp|V%1Zc0};#h=MGXm?ab^dq1 zND$nPk2spn^!QkGj>x%WDI!>iz;0?6HO_i7l_K-<O~x_bM?T!LrkC&kXbDAk-GRjU z_;FS#_hjcE&;fW;q1WW{2pLCJ@CgK=8^VqZ|7pmtkOniANN2Rv$?#qfu87kd^rINu z1r_7>a9;V}0RK;_Xugt3e3FoWz{Sy$m9NJP42AIt5@L^*-j)edGQYE_3~mJQx8#q* zBH+&3X%vD__B#W75tnc>fFP{>>M4wArk~W2Is+z{OMw7<Ot>j;Ua}j5JT>L4L`*zM zO>X+A>(Pj6bMK}Ow_KHy)`3dbnyp+JH|k(MV2Z_dcFNquV@uj2ysMZVOa5qizPDFx zv4~52{#TJ;<Q+0gDWW-ApCmZWALc*k>Zl-(suG1Pj!d?$v*7jI@{_|kJ{fR~D<@JG z>G_<h17zoAGM@K_zoEV9=lFP@?vMB5CF-S148X-8WB&pM@}RXE{}VOGxzSvmV+rqv z>{|aL|K|b46N)0Yc`bZrpCj1D$58#e)dMW2{?;y*<4urQdH~3{79);57W+)ZavtFk zEdPR8nuEAz3%GKnCeu`TtGCtOT)opU-)1jBy%oC}A9ymwmP5_jS|XPLl&hTsph-nb ztH@dIAZYTFXy9uEpMHVC-3Rg~q|Lx&C0<c@Yz%qB@RI{3F~@6D2K-nETCWldca75n z@es^du)+G-(U+8~J<*!9Tfj*%qY!cP>;V~~YbEVK+;rf~q2;{#DG+;{phfvLaZ}xP zLAs0N!B7{D$PG2?+iyzInr?HBA8i0ZZ?HohX(dOr!63~q5uGkbn873DV@A2clv@9| zLj)RqSXEELX$IGEomA(qCs!@j_q$d9_1)H524SBJ)y<8_(>I7lM<<rtLy^NyJXfyV zq9SSFGVHoR75p=HKmML9037tpGo9OZ4S?S1H`{{xR#sO30u@(r<}o-=i1L>*?P}Qc z=EZc&9W6jxbxeeEuoggJCz=pF3tat~7xrLvxo<^tT3IkqRzDzP$K2d_djPO)(iVWy zhKpE?py5A%ghd*H4j5o4wMEIrthIA0&DVPZ+dm7V|5OdI{dc5%4<hO?bGt<+J55{& zG5`FkDV_7&{o(}l3se=vH+07T!Uq-^=s{wh;TZDGdRl=_E`>T-&(o3`0d%bR|I@4| zq>Hi~Ouc^F*1l=+y#+}LaJXLDWSwW^h+;k=@n0{|iYtPMJt>h{b4L)9?nADD0aP6v zz4Z9sr3`lg$Wdks4i4^Olh_<gg}Cla#DYDyxERluty%6#Ljf{k5@-wpa<A&*cX_nV zl>4&R1ALHV`Lp27m$x-0prKb~HOX{J-uY^0vb1n|mAHc;0(8u*?yfe{?YZuKfCM@Y z@aOhIlb1t~VJE6}o6^~ZQ+O2%J-rkVty!HnhbOBn0l9yRt&qvDKEPwbqJoY{T?q;J zr|#V~y@d&0+uPe+)7)^AP8jeL^iM}7U1q{=O!A(-mRr^P5g!5q9)96<c6Oe`L^1t9 zkX5Xt%p!bqApTc$V;|8I!+11pvdoSD#6;xoBA3hdms%oP!s9=i0D{;9@RJUw645)G zb5=Zk7UZ4nbH<qD7@frW&Px25HV`PjjhhV&4o+;YdI+j5{QX^<e0jV%0BT^~rh1Re zME6FpcV90`cy}LtcG+bN`jT)oF{%teUZw4-bYI6}Mfs#w7nyMq+!7+z82eKzjeBG^ zhcnlIDvBfmOuGAuVfy=`ua~Z6mL!e#)3~F^BXr3}islI(XcR6n2srwE{{1^v9A%hR zYaG-eg7y38QIb=nQ~2(~aI|YdSs=cJn*w<C>;b5xkl~!8+_*ewx}Zl@$f0&F14rCz zd(uQYAzL+X22?fol#EZjqYj11QT>rEYVUiCBrNNcamQ1n(5c}*!^pm>jw+7F>EUT! z&J^Xleu3;(?v%dAEGJivI53{HSJnjt{L^NunOpg=Fh;C%F%QwUPeea#e}8YFCMy-a zHaE~kX|EGWJi`|F^J#jW0)7$vjTUDfB=&}<i<g_(><CGp`qeXv=1t{Xm}R{vgi6xC zDroE>j9|g<-skqr?`oT#HC>5=VMH(iJt4ApYhuYe;W6pe;Nh}PKU;rFFLdaBLwIh) z%{_)F78f<aGDvw!iQ73E#MbUDZ?+RDZ#Fd9sRlHT8<ZDL_vYUvVx2J-dQkRj_M5ge z2r(~^!TtqaD01`F4l{-PC#P!w2v6Aw@Gu@V)t`(i7mqQZF|jkLc<x%%amu_l?QVFe zR_A@ze6+4rF1g<Sidg$o+hUG5nh+XalN;AbF$>?cW_jfj0XCC7>0?2208lhOwcH#< zggEObVBrUX!Yv<t?%-E0Dk<OLkNPl|sS`aH+W2Jpg`K+ylP{AsOlw98J?D9$G7=Zt z<6QIcskfn2<#j6<0Mkfu-J)TWGXNM}v0({$X&~`o_~{(}G1l>rYF3WK*T)bHRMQXa z9ZIAK0q1r~;QgPX%;z8AAwr{Dg}MILN((kkr&FE_Kp!bHWx#Ta3Pb6q>c@v!)YF*~ zFN`oqiQL~fs}m}b6W5$NV8?viM*1u$PI3vXRD-#C3${#Rp%(CuE8m-ZolW0_j|`MT zo+qQRL@OZiJOt7a49j08EPOvVT6jECF+M!lA>`VU0}qxjV=6A5CKKE((*M8@y-+NF zB}bDgI50d3#$_1x3rQTE#x7KZ6oWzRrr_Z@9WDdA+FU1cQ#z&oVXn(4hoQpb9;98i zKTYQgRvWFaBGZuHA5QDKWg_Z-+Iy})@Qa{*fKeAG7rk>C@KYel8;0X=*o6;-s6aU> zdHRVuX3wUV?>!Jjr&0OLWDZeHg-rP=_)>Ip{*3mu3sfuPaEJ|AaP41!dWvcu&P0)L z*oh*8>V%)prrS_NLS&W~Qdy6vLfjBN%TG`fg@;YrC*5PtlIvo$0kb&Q3>f4QKKr0C zRD^*nq^VZG6<QorEK#K6jbf94(-f4#KAQff3o8%JF^aJRRH=NtP^oCjFVL0jy8ObY zU07Yn#3)ukK+JGxM|V1CLo0>0S1#R?rlTcbkm@iDL!_eA$gbJ~(a=<K3q-|Jo%S-_ z7RKyjD1AM*Nj&oAavO8Skt82<-S<MG)ljnaJeoO&rRqQ3$ZDkUh>>gz8HVBU$EAoL z{iz-)S?PMr1!3sKqsvLq{6htlK+@<yRIG8_I4vn_ObCMD!Q~&Jh%_myrO65AP2V7* zlk&Ng!UM(i*UO5uAKr`tT_HzEx!2;WQG>Xtfz4Z`86`1`Eu?MORnuWNnbqg+(8p$0 zc?8>Hq>h-8RGwtTy)S<rVxU8#UUCba_-4C|zxj300A%@ffMcNfKhoOfo|!)QNe4?u zuvyl7hG`|J#n$J!cm+Vua^5F#WQTL*4T{?>DxN$YNl@;j&z(y!_a<^l^9CDb<rzE< z<2^;+cJ8J5Xn{5b>S<~2H-;Vf2V$5yf&r`Hs->NfE03y%ik#0mA1wvqSOv@EziE?a z=fzT_V=(pw9p0WI6;advZbvQy;8&6_5?^j_{3LW{-iQ`Fn=vkl`NZ-$<LemNfDZOx z6<f=V6NQf$dK-L*R4yHrR54}vQzza?iu{-Zl$+ffErt6qlLP&}dPMjqHvSpo@kI#n z0~3>g$g1e0*pP}!OX}g`bcR~uHq(jFNDu1-Sq9o(B^l{-f&E6+0d{_wm~>T4hz7z2 zkskuCK|_1A`vo28h~P0?qw3`OOaG_UbT;S8lnNrZE_^^@Pz&RD_C#(WB`+-XAsK|6 zfilJKAQ=~HV}N5eE39bEAJO}e{|YZL1RDN7YL$_TAh){Df9LHFbpYi5Wf7^g{AaOP zM}qQW*qKs^@)f1~(gRi;un(>TX6l2Rtz)Rya^*cIPkV?`)}YW(r>!O}64*C{Vv!gW zf+-}5-+S_C>|MW-2nI@|l&5!*4rq$nr0XPiesUO1zuqaWuwsACy(u?UD3xq<K4ToM z9vmN@UXPp-I+||Xi4BqNJj7EJ%;`t5v$>rk$+a^47FsWB10p%QTL??Zv!!nUKBr9i zYfHl0KQ2_8A(s@Pf+lI&Fob(6x<rvXcH&k|YP>@+Cx*=;)}(xZpzv$@p4$i2Q33vp znD0x8bR7JnlumXgWxeFFa#@InE&$RkJm%$)y12XEf5rZ?!N2%%<V!hxj{F~2JEeiu z@P4TGg+u+xxa=g$N6E`jS<i{c$d6;Zq?vWPrg_+$qAO=*)x+a@b}-8(FD=Mj4l-7? z{vuzy4mNoV$Cc-d;YUj}^xUctWkN(U<l|s^>9VEdn$!D2|4-v#zbND&PL^$ql0q<T z=4&PHlM{?)V5MJA3wmNg$ee{fP>R9YQ>0=N6Y~vWE~8tt!&_nNUilw0?WaO1=FC6^ z86Ztg866!9kotT)ETBk$LHC3#49l!_BCrmt8HAtuVJihkrd3^Ummsd@cE~a!XFXqX zZe7LWo|E(KzzomvgV7HQ9?MY>7A#9eR~xDkl+BCkiMJUjj6|?E7eUBV&Odb;*@>Yj zh|lUQ+Yt}`Jb2M{LqF)oq0PmFBQyhm`d5X=-MOUe5N8&S+dO5G=v{eYG8|KaaKVUX z=-b{5@g3OI{HH7+-WNualK%Q6<Io*cHD?`eQm=0sFZ7LBMuVZW5jhV2QoB?0EA5*+ zprP=(07xA4ea0w4>H6%iI6!kbK<Q^&W11QTEqaQ#l}3^ddAK3_Ow0-D8Jf?VEB@r< zlxr&xruscT_9)pMRr>zqdG?n9z1f@`-5((#&pE_E9}7c1OOVfHHIgSpMO9T?p%|+M zNI~CDr`~<c0y;&Es;{{9R2g~{cfS#Us5ymMo=o3BcO+=9@pzbeV&*Am65xR2%yW#R zg*1*ctr<cx;z~`I!Ei!g?lTJJ#m|U_Ru0ew`CULw4uNuVibs;Z!l<$R&3yt|u~vL$ zFWBGY8Aumdr|R=i`D$gou-(Q$XMWerfz_isdq{Y{wz>Hc!RAN5)Gm;g03KKb;7JZ( z1yGkR0?nWT#cayA^-iV^d6mM;jg{rTu9pQ8rexZwNo6{G8pjhUWpSI22&yZi0NAz? zPrY$J8IvkK`u)vP^-C`rCh^lN;NI$X30RJ5g$kKU;4fD>{7Cz&f#?>HM}I<NZh&y} zF4*&YkKzhI9dUpaF`{G$%e+fj>g(%&DZ2qeX+>EA+Ga(7!{>kwlZBA1kM#?pH@OBT zruETHWjHz_-kQ`Y0n3k>-_hb@VDvp=CT84fzF2u;>pfATF8ib%8Jm~-1Oz>b6fqev zo|77fkzZ3k*9}&P*TX=0k?*dX_?rE&V0=PCt6(2Mu6HcRF3&-V#%=<>ofG_o2vv$C z3jvj+e0rUwC|uQ^T1iyd%vOzpmLL8vphIzG@w@Gj^8-mMzVUpvzc$i8Di#bz2u(D3 zohyM6K=r<Rz}oUJ2c;>kenivoZT;4)qJt-lsRrDBs65AxV+%x+O*RmTt3<XcZgO_t zrRxYEp<qVVnfXw;+;W#<N6ulI;=)W(P>9KZ&0Oh9Mt4_5yGMjeSrFir^!a9U;pr^Y zGC(?6fg-?1s%Ln-2JRlAd2lY|u=HAYEh;Pm!Ojq4B|8LNjO1;~Fg2IIEEIv+WOTAR zlBL{p4Osiz?Vd7=Ww=nQKlmg<DtY?*q2A^R=yo=TZ@V9w$u)E>q`pb^(Y@Rt4L(|n zSKJ}ThG23pYwEfto^1b=iUM?ndg36)EyX@Win?*-bbnEQ^IhXnKXb$ATNUH=cY~v6 z7w0Yatoq)E!EoZ2VhTd1YP$A;-tUKAP4@x<O-^q4f@{xM+M&Jai3@`Po^};c+c`&> z0G*rxHvwOssG{%T*P{K((5+=94{gI(@nYO9j@<^YoyL!$da>iz>x<PVN?!yD<~A3Q zuH^EgSeaR{4Y$tj@AmH}xW5^8gyaA-vIAZ%f})aplyGA<#Yw{u>(+4zg;o&xee|@M zR_P&2^w+!kZTEw9B3BCM)v2+`YC*akh7VAA+i+n3HFu*ol~kJzVTi@2(`UIqcL8O- z$g)UPO&r|2G4)H}uAU2C`$Dg>jeLziVpE72cR#{4d%^N1-@w9H;7HtDgoQyl#UkhF zYz)pQ%&+rsmzf!NTB#o+aC|e_F+*6~q(eB|%kH3_r`7Y^#X?Ja|EY)|a3?DjvDFBg z-#~h!jv=o6%H>x4ThG~pJ>^kq+xz#9&F`+RGjn{f8>=q_ey-|j9lnsJzitZ-?!%t< z_!KnkJH3CsTk)IcGcuVc-3J5$B^{X=1!8H3l}6$p4)jFc97l1Fm_d93g5r9aw{O$Z zJ~jiR<pcx-ytdN1{J!1lvIAtIpJ7WW3cAiiuX5_0H&Im}W73p(j9c2{a#nweaN{PE za|TsR6ywmLVx9!)B*C?MkcS?x(N#Yer9SUyIE};AHa59wUAke6H~s9AI2^lLNx}by z=o~-3D)9CL#y-iqvu2jc2q8Vo+i%kHQlP`0XRR%Gcd?4-AM=5w214LkDc^0<$j^6d zRbrH>KjOS&5~AE){%%?2w*b}N_`cdqM(;X8K|geRSK4Davo)8$!lbdiY@*q~VKQ)( zHc;p6)%&f>SHxTJoP$XU<YSk%>cVW_w|aiJu8xUyI)1mOR#_b8!x>9OYeDK~(ZW|2 zuE>LF5EkNO_}A|vU;~)JXGLs(LeZZTxR!a^1yvr|{uK3kRINZrZu%t~jne(~?kW&H z7<t>J86HYP{26BlTI??392S4;9}!bvvl_oJpvZ?|Cv3I5aOIphSZYJib~Ar0@7iC~ z^GJ}KGaS!aai4S#rMwiEVs@yMwx0*Rf``S*1UFpg%Sykie}}yHDEN4N8*e8xXaBuq zhKHNd6ZQIJJGbe~-PHrA7UE#K#-Hob9Vp0Rb-I72EA&-77p}l;!K5iWgsASXtBeNq zOBv(!-vwywk#$x1Br;hpGBE0w&w}$*IVM=bQGF{t5>U(i<bor2uz1co5-^$dA26)W zosym@&p8K<G|@ckh?oHF^a^XVZx&<ET>yRg?fD@6vA&~ciRa>syAs2K`)Z_8PZUMX zv!3{8$FXs^jXm;dT#P3m_m>JxnK-70h0Rg<-Qe>7`PwmY%R<MLQbJ5nopWSqd>+XG zI!W&>(rv$w9fk3!z*b_sF+%n@w@X~K)_gg%8zegTy$2h{qOrKn5<@v`Z<3QLa!-1@ zuOJj-bI#%;O@=Z|qKN{v4LMS4K&+j5Q_fX{fHJR*i2JZ)Bx~*FzhN4QmVq|UqqsGr zwcgkq&(j@CG>Ix+kBi-kmTz*M8=2i1h7(*`W&1&R3QBI@d8s&_1+Jw9<}C5s1<0ku z(3;A(JpKu$Lr0Qwpwj$=Bc@xlcpuFw(s`5_3wpoOEQdXXcqV30DVI(mk?gj6VdM0r zu<h<mTdyefQHqQTy)m7+*xciAXoUA29rMZz5fm>ky*<ou9*zYKt<opI0GAYq0aEc# zP%;r}T_+>a(P0l&sn>={#>derQ(4DITSg|PIK*_!Jss4;!{aFX{zmE^iiYLZ6r%hS zp&z;|5U;ChXe2fK=3dEuYeCXx-x2yw`1VA#el1Qm@9S43&~=uawXSG-7-mRJNq4#6 z(1G=9=FdXY4?)mCLkPdG)5yO@^#Tw~1M1m#1mlRy%A%Bh77ft97J_c*K3M})9a9V| z*X#&1<t#3_?B;k`(-ru)^IjWuLIB8g#EC2bgisrZQUxeKv%L05q(S!tOHw#XN2#-- z>FjMi08?@O5l=I55ds(6RSjSPk8%txCjNZ;sZa$5PEctM2SAIzFyhG`XbE5G0UZnH zMToL<D!*$<l!a1s{Kv{pj+J<R9)W<x5M^U+FaksF^`{bQ`-=<FwhkC|Fy01698DC} z_xk&aZ|MW_Hz+H38Uc(XqDYa1Ea$WrJh%mQpsz?d*X0E=Pk9)Ise}n6*ew%`f<+lE z{c=ONp+kQ(fS_7ZB96<XIHSc*zbp&iC@2hjj>z#w*3>R|%97b6MAU89IE@wcaaoQ_ z=-RBRH0w2aRso2B@(i*d-OdM;)`0c{Ahz{_u_XP=<&RV*e}C1GHiIEhiW-1KguY2R z9W1q3J&XC{dykd-Jmup7h*#O;jiG$M`@1V2S|;${pH==fR2BxvXAAbtmj!cRV;V%v z28lCUy2vBlNgs5*a5>o;6BD7azP;e11(QwDCg$e5q^}v2;A)0+uiW-@5`mhuQMVfP zvoR8Vb-(2vOmQBDFcQObK4e~iIUJWN@Gyq%I%tzl93XI2u0iXE2jkV|iuFKoTnC&f z3EWii90$B=IsNlX`CPpC9Ep5TPJi_l9Or<s|0;vFhpe1Sh>=)Ci-d^51b?Yvw_MeE zz}^4o0C3NE@GM1<Y6NJ1%65Q0`~22ieRY(EvK}DV`5J7bj%`Wr>=-*juzpr;1LwvN zi*)ah%Y)Y;;h}lzy8X80`D<$eB@TW{K;Pt=q7(_#Pfa$nI4v0&8HWNx;`KJBh7^ot zL#w4$e^Cpg*rcR_g?Nr8gM~iu6hSG3WN^OSWebc7jKKy+bHvx%pRmK3)9tee;_2nY z7d<v95EHKxkKj-zpy(omVf9Z8MD|Um8q7qIbsxMV1K`?=pgF+~28tZ0U#^7ZrLRR= z$^#?T1hjcQ_Dfxoo&hrQ<VJ-c2?0Tq&J5bhS+tKy(BzkQwHJa+<#0IWtuzzFScH>P z)s(zvUz(&xOcI(Ev8xG#5w=PMaIhac;+DeQ%|hdOkDyFHu1<G%IfUPNK!<4;WCy%| zy*%9Q#8XTF68DzZ0|I&o5uzP&wjr4{;{43Ku{0VvD}7fP9obP2JYLfzvU!Y6J^=K^ z-DBb2SjQ%5ZOKiVRUN<ks`719LF5tjEV$1?*k-h3H>VMQoBm)wXik*wk2HmHQcd@I zD69lK55)N^mP(VHuio-Tb46)vtcF`B(XY;ppBrblJY|GX)?q^z0<G0x-TDi7sUy>R zu!R-f=cd9fhsh3W1bsK>M3Hb8iEfRnYpHH8=d5I0n6P>FCadhmJrq6V`cO!+`_;jZ zGG2&Y-3EAW=9G^w+gO?^+wayxkbXqUU3*$WW8s$j1!VEcym*W6_6H}`1AFHaIHglB z&99E=Sz4jH=JV|1dZNvwMBlkuL6UP?5p@`h|A0tads{CH?)LmP23LxKC?Ue7&fWc- zxuQ{Juld?85+`eZG5du2&YjIqekI2JVR_%>-Brf!>P!!70vodTjim49T0CN=&qF8P zc_v0uFaDUfux!8aImz`+@c2l(P|P7^{_#`j-4WY*pSvD6mAxJ;!%-n`u*djYRn>U< z=3>m?=B9<opW|e=6KK@=dSLP;$9?kkF<9a>jRGV)2MVQ%zDs>>x+c5vj?!~0r$lGz zdlJ_g>7Agyh?CDSZFwWyxKF<qz5^-7$DD$F3V7p8wGu}ws}nLfF_@W_wgELQ`I(X| z`_oK*vb?2yV5`1<Ihy;`E6Jn^t>WNE(2)E~S+mX6TqPsoEV;dCnrdW2{Iap$BzFCB zzsuG}czf7GFeJ2@m~<$t6WG$bg<?Q;(aXoC(yd?hIuCA*C(?*V#X-fo-~W<)-_*M` z7V1ao$H!iE^c=2?n!w4P8?$~RoyyMD2){<aGt=gWG;m6O(*7)$^FQ=i4gp;DV=75? z){|fK6`gb%Od1>(*n7H+bLr$w@?}>&v2^G7NG4aoa7MAy{G2cAxxRv36et>6tIgkK zBYu^S(#eZi>k6)KZdc<ESP5wVeLMKVb#pyr<lVXnoBr40!PxZpAA5rBx*XG!b#9j| zEY2gHO>>94CVPTS8%$pWA46IQ2?(q*c&s{|pUXOKi<)NXiGA<yQ+cTE=^4`h)si;q zhwH9};Bve>{VigqdqgblFr&2+|7MM7A==mjdpx@(Po%2%xH03nH@mm@-5o`LT<pf& z6Ax|Zo-DsZi!{k%(Fj@ZMRz0oMoM@^kcrG>jcTQ5Q$DNq8{Fr<@ut9EVnJ%-_=iZv zw?Ps1>F9(*1@CG-Sp9N~`U<6XT_ZYA46r@f@TzYt^sZ`uWR>(z0M>n5uc-|k7C!?; zq2v#ca1s*BGgfEBk`=9{o%WMs%NOb5b57mq->r_f4J5JXJ&&AsTOS}9CvaCdQUMg? zL~sRH`W^dfnjqEr@#djmZwlE;cBTt`IbYPZJIVDLQTbofgrQD?>oxQh_~M-}x$Nvq z(|-IWsu(jsT@$h*G6a`LAlWaE+5|3KHdA?{Zk|OB77Fa^f&sR^+@QnA!{l#A)39#G zHN}PKx($AE<UC8zm*iqq<`T5ryDH3Z3ZHQHa7dK>4U;DwSP6cTpK~o7j=}g*cXdvv zI!Sadm2fzlK5|~@gZud++IEMtgzu&x-i7__Kq%S*=aB&Yod-KRq_J^7g9~L(bINVl z2V&a!<WNtUL}|edjL2Vwa-7{BJL#;|4hr2ICna5SMZ#cL&mk;F=@JV3UL@OpMj({l z#3M^1kVe7O7Bdg%LhM&*l99j1Wq#&&M{=D)`(lmiFOCm;UxjJ1L$DZ=>v=!~#G<+D z{jzbWyB40VTkY$q-=l!;j>jf5TSrXEASWluCbLWlW>){|$`uHybLLa}LaC+MAy}l2 z0~nm($*A~Ih2O2LU!nO_a)^9d-%jE{&CBFiWyK`6G<w$Gc8pIphvVp2-wy1o%jPS{ zl$cDFvOn37`*~~to?IA@_K%Lmd6cdfl0Q}#JCr5#?g@vm9BEI`SZU|WlN8m1!^1J+ zo<TGc?)DREmA(fk(8w0hFDiw%^fII{KDu;xZmQOx_81>SjZ5bV$#nNZN_%p9kmME^ zDrN+X^@}I5X!l2j2d(x1F2Z*Y4yzq~{1$**^+jkgqoZDsXn)Y7*PUC~A?u?|@PgRF z{D3>GisJAC{8)*Q;gLJfr%y&S9K*RUUz0zh?<6}^Fx^<Jjk4~nb&1T(=kpjZunQT) zzGHJqH1jDWS2186(TCXXnD+mftRJ81+Yr#UlNh#N!>If6-G=^NE}LdZH`l^8)LoC! z_FZk9C8t^c``<RKs)a)_NvvtxKAL>ApIvv8pg!GYueo*K)>yEiuw`lfxURLD9qgL6 zA=2M#DpZJ{`l2Ja`NMs9AenF|y&tY!Z7M*YzGCvO&g*HGehm9i8h5OA^=AL~clPTh z_q~L+zw2aZG=EmSS%pEp_5|HpzJQThzr1-u+gI`tI$0KLN53;(b9?lwSuRGo>*eo* z$2E0R1?>G7_AC1Q1ZWur#}eIhK8DJKUA(w;T<!~+2`<N7Va7TlI@bHDX<m1AJR&lU z8mEbgZah|n7%Vy_^T<9~C)=K>%uTacLJty@^gZyX1jAgkCTkF;*W4yLcqH2+)v;4* zT5F<%I(1Z+A>RGR@kFu%y@i61MXbvwXb*bn6tl_}De*o$Akj-apbmn2OJ&iu#G0q( zeqa?S@R$p<_jg~ta_8u{qZpA$nR}|QmN4i@J)E-np$yUU%2HeKR>Y%MP$R)uL^O^^ zWSgbB4^xnjlJ(xpxrYigllblfB-RB@Fj<Lm-<)-9>VK6g+bU$C<*c|D`9xzlA|cjn zEipv?ZC$@tBA3NP=lZ0eW?x0@6;^uhXOHUqNy2x1pD&&B3|9-{_L<INA{3+2d8|vi zsh)n!6Ic}9?s9$O*Z=$|!v5<K$ZZKv$IDqj>JdG{1VASO&bvW57x9hOjyKD@<7I;L z8WHudH2ZjnwQ9K&r76~Q0ZkzMf<~xMHVD^z?SY9-y!UIp=ktt#4li&Sj~6j573*Qb zqTRO)1COPK?e)3w%>PJD(dxlcdxS2A-6_izo7eb!?p_Q*cArIU{(fGbQ~ZN2^GQPB z6X#>oCz`0kE~W+pa?@MRjtx_Z{-<--EKcju$-(K{hBrZOJK(vm3%b7DMj{V_8@DL9 zeUMWYZ!`-sNYlYp{YcHu<LUd^E;M%>`m2J?SmOy?5iH2=gu{^eP~?U5I>UtDew&>l zjy=Z`Ne{fGI$v5!YHtl7m2bB=?^y3RjuVk=9^S_6t?`VPxsWU!8<>#1u5ahj)*xw> zhHhR$KXB1j;1O!09%SOVt+{4p?#~Ul>or5hgD#e?>B_)^!~B=o`{X3&bnxRwxcQp4 z2UWk*aIW`qCd0eFYQ1iOr7!S0YP~SBex;T0V|Su{nUy&2?Olf~idisLU2&ZkY)?i5 z$p(h?Nq91z%1eEl<Cys?ZZ`pa=K!T#*d}8SAFq&_sP3y78~cc(E8C@8bNnmUDsQ4@ z3;7E&_nkia^$h2c$;Ivs+G2b-kDj0WF#oj<U(vK>)%D#+nQ&&UV(D$u0!?1?NtrKP znqRaw62e1NVv<*d<M>r-MIo1ee~he6_Uy+28CM^uwv@N8FY0EBdN1mVy)PIT)W&{Y zwifk1K87-}3!7nllP1~x`Jv$R0}O49XBhG(H~C5{&0K$E2N(p4EErQ5=bGTws-lNu zDjcfrj#L;t!Vc`Y0h>Gkq4cQWDp@$&XHjUcg+AQ8LTyPtw3&<8T-Nn5R0cJy2&ob7 z_Tny7(~lB`d0)O!xyTul`)AoglT<aG)7bED+1Mwg`{OS4UdbVr#Z~<T9NXmvMmo<& zn#9H3D68nokv@wx?G5a|=xXY*oz`c(-m9(UdcR-i-lsy8yzD+zXR}2*CcE{DHnSa? zrMo)bHT~>-fi<n(T&Gcjw2o`kolR@i<kYk{KBtUDXKiKtFXY{7R&kc~0lRL)93!bA z$@wQut<}^zm&?NX9|irP=59j6LuF)pf1l|8?8=W{|8YK6=aSg!M3mf1$l`Lio`4%E zdw#aP=cAnTwn4`1E+E)x^A=~|n#$*M_#=YH(`#4snJ%A8ZQho$p37<G9QZ7?CxmZ- z0p7{*lo0Ghf{rO5P<`4^Wny4rV?rAb$LQ<9jck=Bu`G97Gii3*HC=moMDs2W4kPl2 zJ)w^3Wmj8@g&v_!M<yuO$J$%;9th@}uCXp;3U=9(eo^!7<xLWdgj-Xu3Noo?Meaw~ zXA8=%CWem>xDy>Qj7TDP@fd|ZF<G;1)+7yW#(l%@pbrwzlKY~;33MgOKJr?r-WfBJ z><oQVXC;usge#mk+lywEZ1rs~Z}v4rU?mh{GJGTHG?iZ~ja>M8G*@?RMO&%~x?|-b z&71j>B=t1ULEhh&Dk0JD<|wl9;9w|?C?(+yY3z5MWN6UE!8)ns>r~~p-PdvDc~mU} z@s3z4^8<C*9>L}`c8_y)R*AFqKMAIeC?@<|GaL`uk8G46-j<*|*rdrF+#q}1H)>x= zp2|KQ6Dx(tJ(Sw|F}1u&Ug~Pe)DSAX4O7!e+Rj^9F!C6EcivK^8rT5+G;kKTJS^|H zU)|p&gjJn?SzhKgQq|qXxC-0W`Q4XRcg^Csxp;bOANQ`pU(n&+Z|&)MCSHrF&!!;J zIc$SK#$=5%YasbSo@O50g{2gedS3+%<cT`gT><)`M)*DfiK85O!f7U6Pbc$JsrAGM z<Wk~k)<^P=I7e6>Ip5%$bQIDfNBu}EAJ4;BkVC~+kVrhz_p3qjX*mw>NAf+!f*daj zO#e$_cSo7W<gunm(0;V6NOxNMI8Ei1Izo(x`x4C?6ix8!L%B{AN3pr5evQ?B3(~3g z*mYFUe&w^aO;?l72bUjqV-}^&?>-ZIpRW@+by4c(1(wqHl``w<4apTlf+cAfU3w;D zzpU=KCfDlL2bYz5=}qUwN1F{NPTyzIK)`^rg21IoQ(>jY8&Z_O>BjADvbC)1z2s(d z>;&OL{gNhE!(+lboNctqDFSZ>0(`ZaO!!P+ZyB>+L-@&efBJqBFo>)uat!x%!H;Y8 zxG8vc;K$ce(8nNg*;H(Lv(87YL9(rW1xRTSR>O@G$wmSks7Jk6*MFES+2*I}tc@DJ zB^}Q%eqE0DQaI1XF*Xocq;MPjO(osToS;u~_=(*4sQ5WbDaYH5wiEJXzB&8k?x6bX ziuiiwx?MFE=N4r<jLboT5Wqfg=0nKhF0BdqNT!*08=XuUmyi2?FPZK|M@aB32XW>} zZ`J&zKEK~wICvR_j3X$G?I_ce={Co9DY|O2=yM*Ar@;TFynaV!9?f(4K-w8R41_`h z6BNhU3XmLF+xd|l&;7U78H-nED&&~J%)dr=;c(rE;Tu~;L7{oS$SmG^NSR)P!=m$u z&}J3Xsmf(Cf%Xp`c4F+IoA^5B=GC#~H<%Y?f~N50&bJa1urZ$#mD#Zz7x(3P7GC@o zKv``OI-4FQ^z`(p*Xp^==cK#YS%!{xp}r}R><lv&NeZB2UAwMm8HW`as-d~SOt6Tl z*&EKL=5@~#^xoOh3}iT!UCykl9hFv0`(&L}p%yb9-RP^XH)qrJJ%LE|_t9q&Hy2fE zzbV0mYwCMwqw094X1FiZRNd3`Y;8$$jar{?vg;2BO=r>G>KI#hu0|&-alnUgnv!eh zJWH)H7ioNIEm(>6zvmJ{@wv$YR-JTzWu7eDzsT3-^!QsWwZ0_o)%)w_>$cM807mL2 zJAuD$MO+r6(os8;@7{j1q4hIY)VMVKNX4l8enC>t;}Yd><nw5%iB*GH>wTZKRPdBo zJ?m^d{Qx@{+iWsVk-1!8fcVau?>Cw3!0Vw~Q$3Rs2_F1oty|q9@qY0%$chPI)>IoY z5%O13;WJZyw`lzZm~QEPHLXiOPc|8EeZHVB#bM!X`?z1yGic;y$+^#Qy`yKfySNw* zHV|&4<9<kc)La^E_uC$4_KPHEAM5|a(^&>Y*>&MsMGO#80ck-(y1PZB8M-^Aq&oyb zN=k-?A*30)JEUXiRJyynIeXskobyBf7(DaL-g~XR*1fJfwCJnFLi84u*I?}HsIt|~ z_0%{vIU#`vJXn;ii%cZ5(VC13tt!2a`iz5$MoE9pB=y8`Sf0;2K7y7L`#w9=*TJGD zihf3$yZQs|y(;+|f*9y;@NvcC&H7WDKW5AHsA54|?we%yYOZx_kN2)u;*GAQa(1iZ z=!)u8&uyBItv4NZ?8;VSwR$QiZ&PQ4P9E}{l(gxPvx-}}!XJcQn6BoPm_K)5q&<Bt z`ih4@UzsF21V}TX4M{`^aWE!-%Ug+#0AbWcc^Mho7tj=F+G2nLajv=XB7sIkvsmm3 zj)W9tFYXjdKkJCLZV@?tf<dua=Nc8rDJBXv;W}=OPr|hQ3AC7C#S(>aMeb?BezQ!K zrq@?aDjK<8gExlq@*_%xFw}~l%SO1c6n^iMSqhUZ2^&Xw%KA06^swWsO8CdYV?{Lg z1|^O>wMh<s=E0yM+@2s3iFM_;&YlB&3f3`c41%#q_aaHnJkTGviMFHY`9*w9Fj=C7 z)2GE8y7k@dYv0p+t(>YcX=!LBuMssbUKp&BS8d&(Nc*)jk<nMLNU@gR3px$PKd0W{ zt}Giey;6RIjYZg!>lJj5gNWPSzvOXKNDy=L6h*sHF=4qHUB*36fxy_IKQMXa-HAMN zi5zxHBb8pIxFXheYZa)a0@IXNiZtl>_RI2#>^Bz6&ILWTGe42DkS3$+xObt=M>y3f z3uQi>XF?<Y@;i|tt#@1nzBdzk^1}RIo(h=Eg^`H;wfp+f>qQ*H@Vb%MUR8mf9IN7% z1gHo6kT-IOQYObkMJ;MkZeRLTs+M`;T&`iFc{^0!^6YbCbTl3V-$8X)PShjzSW<c) zyM2jeBPZE#cM$`~kAuwF4fXPDiFLD`@_oU&fG2a}p%;M)G`r0If^5DY7mj9Ky~j8h zXutVLq~W&>2an}AvB2sv4DdNY24B4za2aQYi(1Bt-&FFG<2k6VZ@5XnWdeoVe#i(l z9(KdDRee}ymZ?OW7{o~R74!Qv(@EOVtu8uuL7H^P>(ryZoE}?$JVt|Af8U|UosG-E zF?6f>#m&Q#(@f15zGD!%=YE7A!2CgHqmbCUi@|S42O^mH2odtT!whdirR+mu%^d0e z2b1|uii(a_q%xW55S(!Gr<s#Qy8}j6)UY20$|QLd^;N2Qh-gPL)lR90a*8#^vM!hN zYj0FYBu&Up5yj0?)_WEj4f^5vGDaRw?$;#-)x8z&X)J#CRx*6ds6nV=Lh6;Uj%`QQ zPwC4`^!9rSCTL&(j56F!q93(w+yDMbc=*e`jhyN0=Y-C4vt-6`O!Gfnnj201`1uaL zl5@jZ=;BZmyi5J3Y;<k8jAQ)OCGtnw+1hJIY#DE29-brUYj9JH%$1)?a`+CrbrC!L zyv$~pu8a6fr<8G(c3`0J4(-<mxOrmJaCR<JwwZqUN1gmNFnVN%BbS+#wUsxYfS)ju zZKVYdwFqFr3AxQO&4i<CagyH|yzbU4V(EEH$LXuU_FKFCQZ$invZ|E^g&Z$N1~+_- zcfv<snQCm;326V#HG>($L(o^vS~jHz5<eA|-}u6iJGPGc%RnL}>Sb-2;lK2Fk7;Vx ze})3{xda<J896T<Dr;jLm84lZN>75YYd)EwT3ciY@`c;kjb+-2@SMf#vR-6stpKTC zR8rE$I{E54PVZ~+vuLkd*;w=eA)%cPM36`D>=-K-L+9Z4qoLmRtw4LIN*q@wgkg<3 zUcvyy25%hI$pbn)2C?#@x3(D7(dFc-9G6t2!Q0B_q=amZ+vL)Xbv~l=wzP4h^zaYO z^S>~Cj50Nj2>l0Pz_E9CPn?$aJ#n;)UM3)3ZoU!=5IPG2LuFlivl%4*r1R_RtB4~U zx~!613BoL~o3`rDwO_0)mPtm+ynZ~{%C0hZ|7vZaRx3sfeousHW)K)WChw=It$`P& z2E(Q{(5W1hwAds-GUMygGm=%L;0K#(ur((@S<9|kD<Pxi-hk)#c|OvE2H0)()DEY< zYpyvr&RT8Gxg%%GmWwNHP9!yVN+0an+;mz2JtBFr=JRlVFdG1xD!!nf#^+cQ51YCO zWS8fvblQ@TO{;}-Ea5h!722Q1)WHjlQTw}RRZq9KXwSgUyOPM`!Mk)rHQ=gL&Scqo zYBv5=H9=L#B4ZOR;)*85K}ovTj^BFURtXjtC-+1_<J0mS^!#+_6?{jln1*`DQvC<3 zodm>b9`n+hyy@XUwQSs0AiI8Y4%xXCTZ>Bbu+#_isy(+$x3VGLSD_Z4XU8Uri>)ot zgPDE81<CVaQ%TwySelmB7WvRjR_K&<y;YmF1&qB-@WZo0BIN7%Lt#WKpNbtL`C3tL zcI->0bVzp3a(0zM##Pz5u1D|O^$vL;k$nwaLk5>aD_QW*Mf#F1i<U1L$#|}Vh6a=( z-l_yj+?FpvNBMa@y#K|_m{<ZMk?4`>SP=t_lOo0F*U2!8b`RPY_3%O^8amWII6+pH z6v2m~1n|S_Bxtda0~vsf@ZlMI05h~zn?aSUvah+jrw6KWyjGWtr=@!67gAoA;FM() zxN96p%!hdAu}5QxmND4n=H_p|VGxMuh|{HAPxkg<@4AkCeVz-`w9>6(=rj!5ALiA` zBN@@ZYjEoT*c_iS>QVm;iMvM}51p@zJlR9HeCxhh8KkY$RZW!na>TeO5#~{%ids@~ zfX7kA1t-vWt%GS#UjY?FtHKV|{3{1swJlp)nGai#p%FG!p$wVt%|;U-tASh1lDW-{ zF>2JwbT-`kG+dui<6mweA`?`pBEH5~R7@SUozElU^gJ2$Jnw4hb?Vq7@D4-4LQ&DG zR!@_uurIwDwl7u5H{_~>$KbUbFlTG56#an57%1azp$RbPB-UOvnp4h>jlAo&>wWIV z!!-Hd^NowLeCvGC(*@Zb`8zunpxq+vL%e-S_@lU;Yy7Po^A$6btr8y<l^)$hZ|1YE zfZ4Gn1{0@Okqb5KU%K4@)u9fODhN@eRd|$H^W*-;C{hMbNVmx)Sj^hm8uZlg9h6|) z(T+;6b!>m951E9%1m8sxM!(Rx5srt|!JVW&fn9Gt$c~Tzh^}!Nu<xy=D`LjR9O09{ zOKH(c<#ntr;M_Nlk0GT7XE<`N?&ly@*H;MyLNSonV$8V`qqa)*38UqjwwJNG?}c2_ z?Up;I+LZ9g5ErgWQekE9iTfc|h95QExhf$uF7HBMQ!$okfd8ei3l3KE*`&=a=jgd! zu->5K{-TT|Fxb=<nK||gv^h%YI0*%XG;HjJY|blRuE;H0B}DlsbAeWM(OB|v4Uukf zifjdjO&r414;MjpMpv3<+^+S*4$P!nxp-OYW_|d68O+|m;*JF?!*{9SwwS1ZpYU!y z<JqghT3Jz&jZ3=OSTDxL#_mb+*kuqW85OT{+GhluA503d1|w?=ER>Ny;Hf%3x8pj- zJSfk5k&_e;jwjw02!r)_LaxrgNK~#$t-33kMp-NKIsTc~=KrNLAK(9Z`>T@zYr#;3 z+4Gn8sdBcF$;U3}2$M&$hLTyRM6q?7G1}5g6cbOgnv*djEE0EaJU6&j&XKjn!)g~9 z+x~vYJFd!jiGCQ@JjXlDy7^1R{5QQS28)_tq%dTsaj@w>N*~a0Kl=H4FcZ}w{({V~ zqc6kaNv4RGuI^ummHvR%zcLK1b5Au{dd(W<XDlG?C*O56TrFgX<gj=|{7vx7V<T(X z@B$i5x)59q!6L@U-p_fUu3E$jQRAvCO_V5o`LVJVo{S`U?NT-JJ-!}tsPXH>#KuTu zm8lyiSQvZd<veN{3IuIR9PNwPF-r%awjQK*GGj>%)<iVqZM4O313sUy*`wt-yUSpl zqj9eJ-h-|?I;O^pq_O^>xi+x?KIgs-m({ar>zJp37*V1jea)_9f2KZuPf#x46+3mT z!OMD2oXBaFXKkTnlG(A6&50%VvvND0&>}<Afa0ZS96_G-3u_C)rEJcus!Y?rM4?+` zAoDGOO%Z97=@(7ybUhMS8wcl-2-PO5L4;$$I3(B!oH0NiB534FNUpKh+=8MY0(2$T zjmAquJ0<jZ4=?#VHD|ut`~AdTvv3neftS&IdI!RSLsS=}Q1AEw8SZRI0PkLuMvZM+ zxbJV{<H;azG!(8u&Fl{Kf=k&K8SZH^FfN-EXTN^+{!d2W5JGf6&C&m2f;a9TE3S@L z!<%1bZE*_)(SxQXrL<O(7S$+_*hLxa3(bm`rgL};1?q#duI|0IF8eGRSjO_=LXS${ zo1pj`200GAl*!2sjfG-0lr3)j`%lzZYL{)0%hwsp;Dntasaf*IjxtT0f}Bl6vphwl zGHY|qUj?Q?jDyJn8s*QY&BUK?NzB$I)ET6_&5yJmqbC?E&Y73nohs#`t}G{$Y)VTE zjSVT(l&rgMnqJPH$g(sU&FUup1=Ebi2I*7-Y-$bPe1x975iHY5Z1daLIMcb8`5~DW z?`2X2b!NvH_3F5@pU^1hz%=Cs7rl&qUk5SD{-d7nGvE0h&T4+(dU*9Y{`Ck#Kxkh+ zUzMpE*ZU%+uJzN*7;6t7$Tm*ei`8TJk-^NG`dj!WB??osqO4GPbHPBEmmw4S?&;R7 z_!Di%Qf<Hy8Oe6q27#L`Uv=HAts*y~)?8Sh@_8Jk<N?OGEj4aj(Rwy#onzW%Cv*-{ z2xhX!pMOyFw?s1**7c$9LBvH%spAj1{iUA;L5$Fk00zrcWUexsR#hAw$@FRyV;OA8 z-DTzRcMaPt+;*#cvC9kCPb|SL{GsoY%S|ra%bUuLL)GLR37O)oaf6l~rx0>gQiy;{ zRdU&@4A>NJWg89+u9Ty;h*70dr{Ludy4%*z*Q|TDERoRHw+E$`pSGrO(Q)bJF-h2^ z!Y%@BdO)~*l1YhWw0WE5gX643sFQw0t_E^EwF2hz6tPnur$<f>b(S{c(vaaRUQMYQ zI7S160G67mz)bB5nynv18QThbe$7G{M7mtx<;Bj`m7>gilm4Y@jL3Yhld1IcHCiBF z6@X~5HmwZ#O9al16mLJnU)s&{{kgc@>S5>aqEt`10S|er2i;nm>IV53L8L?Za??X> zKOp7W(hi=dy31yy$)FU-2=}~bxjyPeF!cbPhe?wXdnjP$jFk=PhWhc*rr$pagP@py z9Ar9v?4YBQ(_u&%)lm<(G9ix^<M>21LF=RxlA!krj}rU@;f3rf69mv)&rpQ<`h1F5 zCQy5?onoMshxTbzD+UfX&(lzoHkop$F~&^BGni}Vl)5TfPjn7`LCE^a_;Yd4@(kyL z@ys7Dh=l%w%e_>pu5MfHKKN<(a1aAz2TEJY7U5NLJm+V;EgV!-&i-sgw)=QIrG#p+ zF+Q!nKWFa<IDMgb*!6IXjysGCn~_{yiKhf#qAlI_uE*T-BF&e<dgf%Z1J8XsV5aSZ z=whjb>m+BaGiZFhtobj`2`8?KsyLA`|4R`+riHMIQO3?2hZJUHx=>d=TTOQ*ei0qi zZRA_{SCy7ugP%y(-Ak-L!1T#dGdP{)50p7Y<Ex<w(Dw{l>_$}2Ao|+c9M&@*>s+>J zpOU!86FIj-Bbz_CQ$B1=*Qr0NV>bHoC0iDoYbn*q+Tv0i<Y@Og1mozN3$tTYeb0(l zh^?k<v3vGGPVt8g<Uud#r!?^nJHde|00DIYS?Ox`Jn+n@%aik7Q8P2+WsbxA$v;@! zmd?vu7l|s>!rp41tAL<O@zme&Z08?R5htyyn^K@#DTSU!H2IN?gBC_ssS|?6Raw41 z@Za+$DaIy1X^5+*Wq}%-o^QVnRzMRfI1#_{tsA6pt7_UbxO$#H*)7+w*~s@a*0W>1 zPfW9f7poOQw*oJHj@Jh#!%GrQ7V18Saq4gfujF*aD_PW9;)^*6&o)(+$lFf@7}Ho= z&?$&tlWUkUb<j|m_`vzr{5rGA;ZhM`U`uyv+>+F3n=5+WJ8=QqU)g-Bq3}xuVC&ov z!bs45FjW2hMCJ7jU@a7jy}l(^%(IJeRWeeJL#z<SfWug7yvd4xy615;f<B1IXKLQS z4&Ts;%FY^(Ep8@YBKdaVqjYpfg@|Q9rx4>u<^fL$Oc*q)#)do2oQ%L-M51kgV~f`E zlR2KsbK+(v#nDVru|)PRh!thQ3+h38?K?`QIOpmH<DNHe>&Y%Fe^4-^pJRP^oYt95 z3IbFDo2i7ZZn(_tGOqbh$<LjdX|Kpw=q7d;c3grIWrxYyfnK%fos$-E^~1)wx%!79 z-Cv>2(l({Ny;Gc&CbPxaUj<WwT>2cqU?sEpcwUU>`EIph%@?EDvA4aw;~?9Q<-rCW zu5qi_>vxK@hf{ExN|Pf!?;ao?qeO}UW%0O{NRas;SpH7)YcmZue`m((?5o1-U#=E` zEHz#n2d4;%dWl-b%~h;Oj#seii(7dGr-{@kPSK>j0XkEAAf4>h_1L8Z^rk4J9<3SI zXjFkN`tz(T2=D1h-v(;!^#_$!4w_6Qy}ZKR<yu6^?{Prxft~a9K+TTj2R(Utim}{{ zZc5F*=BC@2f<36(zQvuo$udpuWb*!(Q`Q;Uix_HeT!*WhV4nf@z>vq)aziM^35ir@ zDDxS)3Df{urjoCUQ~z$&b8k>@LGz{v4S&D=*jl2~;jq5pI4A^WQ!AR{y~o2-*`|tY z9>`ug*lmRWdqw@Wc8`~;>0AgtTl2Hw=HiFF@*uf!p~wB_C&x{qA#V|?I%g_a<%r@2 zvAx=BS&b%IChl}Y0`~3*zQ7q8uDJu7*;E#zfC`&=U2noe1o#q2(c_9D=xn^D600rN zn@Ts_i$>YDdjDTd6A9<|Z^}*lLN;v6$Nmt!zc|a{X5BNJ5qaww7Z*5et9OXn-J1qC zVc}b6%^+otun0bw^eh9vo+`HeZIBn&aqOWy>SY`3767H;+v$pkam#c%7(sSc?~mpi znn6ekZBB|^fS>j^A)w^868an~hop@;ZcjkOZ_K3eRiu+)#4Y=N0Wzl@>#2wuI}(ZI zwZcrh^4Q3@dQH`0?GKl=Jdv<+Db+%CT2xfGGWQPF*+Gp;Z4*?~T2|A}`J=8uz0Iv* zzTJwbI*ATB?&);V5M>cvAPL<Y<&zQJHaR&V)2g-4dO4^<p#|6;x-jM<NxUvHQ<8FQ zZ<2xYt};E%&iOLqtb4LH>p089of&le!KGiz)!t_#tgScYaj|v`Kt6Xji-<aWp^ZSe znri3ShsIsj-2K6Mc`uVpTgb|m=>eNqd>$t3uI3l6U30^1e<ZXd^_FLGBO`P9jF>`M z9bc*T;>V-t*T04C?!=w9%JP#^ozmE?r&?*;h8tbf4;dm&q~kWoJOE;)`sdv9KLC<f zu${3pno&Jw^S3=FW*Bl~YV7iF3H9jmn!Hkmf6))CVsbcFa3kmnpubj-8p|)TrQDg{ zv?MdwXd=}7oQG<f_hpjbS0?$gm@RxX2S_4yGdvjn)(wCRDP`YY1`^E_g;m(d%Qb!y zMcFo7Y`TM8hGa(3!Bp!wyTHh8Msf@^$#v<cg)@YE7n4`RJay>MoZOXAPBBb>#^GGY z{@AJ-;q~+!x)+#Ax?44?+h|qbq{;BRSxhWo-t_UZT#ojP-74H39CHXIIe{#J9kVv~ zB)WJ+NT<3<)?y7LL75+23qrw~g4gr=^)Y}mbPJr%Q5lIfQw3I<(W>p0M!not`|@}> z^IR}C{pxfzA#gtm>x!7lQpYE{o7vaL-npA4r@Jb-D(=oSmFX$bkj@c$`Zi8$v~A0c z+tFWd5`~T%q@)$fN8a?$DNe_DqCu4PAa-%2ZkChN_jMPuuibRSd;96a>Yi{6*3~&{ z59!+q<x0`{S5R`iS`5M-aigwYfLwbgfnjv~X?+`c^)Kc>u6Xg!8*_8GDu(e9J_d$% z<H_Il<`uY5PB&d~BjYzfy8^>p8g76aqjGt61E#5va6ya4AVVLg$5Ky-?V^qNQP%!p z5OP)wRNzU_qEx&j=B<&5q|tKOA>+wc>)sdgq~Xa|HS_O{Jh7bV)N79Ui}^OKwJJsm zTg#c1j8Mew&SjH9Rbw;kdbngxS!?BbtfIdQYp^HxiGbsYb%oWmst18GmZO%*IQg2D zv2mt!{@eb4&p%<|sp%J~mnt@0?s>TV!>O=o;8bKC%{c!Zn~*?e)Y(AQ_U_`p`BGk~ zFYQ8e6xa?!Qr@3C{!;8GJ}@7uki^=RG20~g3^EM}3jk%Gip&<R4ez<k^2TWO@a<%C zs<0|5X8x%p>_#959BJ`^nl}niZdLi{zU9!OiEd96W`Xl-q)3O)>2zn^d*o+8I*a85 z{j3=s{xDP-trKYf*RX?Q$}BoqH!F^Hd0Y5QSJ9Gdh#8P&E&Z`)Y&2+3c0SM$Ui>|_ zRoafsD5H7fth9|5PGPU4MHWtb+1q{|*%4AXl%p@U$hJh%H}^55B0huzwxWaeJ2VNm z2A*8WE55G#8HOwvd2m&ZIRz-_{MIi30rU4;wBb*>U+BP9YMXbsYRZD{5{cFTPgE8D zCqq8^X@*ZXjN3nXJ|4ZyK=BSlDswGA=iim62xKoq7HEU<LCMHq`HL(aWD*+cjzNe6 z;}s1t$=&jCTrx71h)VGX?1cQB;tyVwOH%397Hj}y%5J7ZDY$8FKE)>@Tzkqnom(Ad zwkV;hYC?WTj&4ov1zz$`lrYw{=SyDoI<qp8@b+f`;<ICQ4hKV~pdNY};-WN7|AebD zAKZ3y87<q>Kv1<G7R7~g^_nec$g}=6`@wAUBYds-{!VMHFCin@D#l9u0}4>F^})F0 z(2kCcSIatzNMCx88>So9Y+@73e9!z-HCNY<c{pI3MN(&K$lElvzOs^6tAuPaIhDD9 zwoWZA)iX(3?Hdm3v5)TO{GW;%_vXO{H<gsNJ_&pFc8V`g&4y<;lx?LogfX<vmeYbW zAJkS_|2zwDQQ~9bk_*<Z6N531`4ZtfD`|Cos3jOj3p=YU^3sh*_KQ~YYxOEXNi8QH zpkK=`jut%N^cBLM;)LX6^xS+-Tgq0y^y{pGJ9#HUbl#kTnT=S0Cd+hf%a9}c{y__U zdW5vCV`N)3U)8EVI!0hHmmtW6K;*{cBC>gR%Or0ys5kprqa$?!ei11eXexzUnk_JF zj1D$O8;FoZuw=|*%n9%wu$DXaD4uqf+O~XRgG#-+cp2{vO<W_0RxRk@hz?};w3w8H zW^bDNT-rGPlsXP0w?j?Tizxm;3Kr84G`^SUSscrDj^FH<xO8xS{+GgDWX-4ya3M)6 zwuJxvK}!e51r`b{z>~nx1}27&_5_OylV>I3%`^a~yayGI7$n(K?V~XTY04kV2WQT5 z{C9pU>#M!JI0(Z%D-k}E^0~S#Q&IVcjAN)-C#K35!OC1}W`%`)J%^zlY7HiRNj%bk ziBBbTdy<i<nvOyx&=~G{w%K*m{7X>zmv9Xx=MqWN!wHnB_!!tJL5$f0JlM;v99}02 z>6Ghy8pR&tG`rYM0!X1|^~+B$jE;X8#eqCVnwPjvSiPd;kMZ@B)6<zX2CXM)u$R2a z3DobLDWcPk*>g$k<fHluCL4a+8g@1Wszl*Gssn?;tKjsD0~2?0fMW&ysQDVynF=4W z>VI_IXo$TQ;Ed>wHW;z`9`#y~^ckhhF9)q^1J(}IFsF6*QofG?z`>yH)#siXyX6%y zfENkq4Vq5?WWovby6U8F#{RT_c&nW6teLNpJ&w8g=h<hSxS~o+(bRW$5^<xBzLv7q zO=o*l{CmV9WYO@-m=g0-<`zD4>@te?<N~uQ5m`hJlp}5NZIT%|9ujbb9<4ngZ;WA> zRwt)!{{8VSERH*aAgxfPSXW2q+P9FkHh%c5mWOAn<hv7$lL`zVTU!1$X3m2aM8owz zw=!=ck*no<sRp7Q9(zy_x%nfkgI<$;YZK7=k81BU<%<Q09{4BA*^o1B*DG=l9r^Vw ze3wh{iVu=*I!}+!(o090(Lwyak<ZTsVFY$75?Bl7fAg-sG=uTrEV)#{tDQJ)&(h2s zQ;FqlhRO%wsC@;@7^S@xSBJsrTW=Y8Xrsu85T1#3GDp_KFZiG(q)@wgfUCvG@~AFC z^4Jgx0v?ici5t73Gz3*s?u6!`*f`M4+`RDf;c=W<AT0~7CFl=fkzyorT4uldt!a|k zwUQkh)12@__-QyuyH~V$+ieAeQwj=TQ)2N<wfp);ywOJfX9hqKJeG!*K?vwy5>n;x zQ|uRC+B3chAP4s>#;A2z@KJwsKBhZQaTwsS2NShV#BGA;H$(0gK%uL|I1I1S8au-> zvn+xk>Vr&#FmYR>KJimX#Pg`K4s*PYb(#NWP;F?WoAvE6vbDgpQ3DZJOBTJSV8#+| ze+a^R_rYS@=|8nXT?P<6dzMfg3=?NiPU4Y$&&CKjc`v8-ewvVtr=aB!UZ|5FUNF!D z<&gv*Z9o20M3WcQfUrRVNA!N~^3vaaL0DkL7o92EVL(tzoHOjgG>lOf2W|W9|9v;V zbzwp_DFr=)aS8bjM)peeV&c>yYuOXIs=3p4O7&B#i&>W}HE^eUW?zNmxtH63EtG6W zKPIlltFXo<j&P?D%OJvyMM|%cqe@4egf<&1F;PGR0!FMbtx85qLM$Htwlv+uyP09u zVD03XGOyXrCMOigvYB{>&*2>GcN+4N*Z0qD4g~fAF!Czv2mwfG)^Y4nmhWfd_nZ?A zH2~V=Sm7^eQ_?@7wVgKuS_yO1s6GybtcAMW-Ze(;5Gz+!qt($iIAu{wG9yDGRnM~k zqn=2Uqr2;6;}ehlZcB2#n7)$@^j$}n9lt}p?rWp2Zod7d{HTAS7gRghRaLxyf^hV# z3CGb_4bh#HU^Kf%j!GdsXpJ3&Y*by$oUx$1scileHg)nMS?x7LybuVq1<#dchXTHQ z{;~NKL_@)t7X_1b@nFhIObkpmIi+@9g8`%!Fyb->^+hTyILZiZ8Wgf+5)=<h2K|KX zi>qx3I6sRSpE}WsXdsOd6ewCf`WiRh%2aK*oysS0XErB_?@sDBApu}&Ams$D(@bB< zP2+ZucgbRfqsrGg@6u}VnWVFif_B@@4UtBZqe35=uSz-xiu=}De{z1zLcus8XhX@X z7U>f2?MXOl&vi7|bo9pX{_|+@y<~j(NCTVM&T(sAX=Odt3(4pg*rY0<L|e<|wEL}1 z57Lk(IRf?JY;+Dn@d<~px-niFUcF)FypT867RN?*O0XivxKTRLgy1odeZFfU#<4*v z7jWeufQq8ULJO0)wg_-B>l<}#UQ4u8-qf#k(soF*Wo}+XGMB(iv^LmEm>LxpQ2Kt+ z!|pfutF$S7nj?pGvaDj_l%Hd9R-_Di>wd0)30LX+uKnnrjjb`t#X|ehflvH!h&G3` z!X*<(lZ*D59Fo%8PF<!PsE7?(PH6b}w6gg+42FISC%RILueQ87pf6D?7S4#rLDhpM zrYkhQx<46%zSuPaJ&jWAPg~h<&y!+!`DE=b-<lBF;I1sLF=#5gF9f7m|N4gV(+6aQ zL>C{nxy(o_XI%!m8CTQaMk;&+@zDVdof4C)5)o@&15_{tZINqJX`pM%A&P*TwO~#4 zspNAo*+_9!#`wQ{K<{T;I_SA55h(Su+a0YFY;lI!dKb(yDPQ<7a_Ds17%9N#{h9a( z1IXh5-x=wDgPa!Z6`j<h2oyN9q3NcMXbWVBdH3tocAEX4kWGTphu9vw`LL+E_3)@Q zulw<3_OFh6iaGH$%gH-ZxB)L#jb>^EjHi~CGylEqU+KY3jedg59)X<g>#vG0CF%D> zONn~qV5N{&VCQdIppV-vhSHy_t6HGGc!$xGjyE&*_TK5z(282}ZKvJ-RD%ZFrh_BX zrpL_lk^N{TN-CkNAUbO5$lH47sHm&tg6u#?e!N3WlMLkhVs8GU+XxD7l*oJ<^8MW% zK%RlRDqURECjU&Hfq+1@D2Dc{x0EPRH1xJ5A0(6~EEAdm5yT$MqCfRmbpR-c|G0}r zouN?VWF5{gFxIKsmgC7Idi7<8i6rUBXJ!rC|7p1AM=li#PQ&|bA?rx#<_2YNwYS)h zKKxyX`G^yWhw~U@zzz}}V&$^LJu>j+NVcmJYQd3J9}a)fx(n@s%lC8LJvt0ZcPU%g zWn>9LE-6X_8NaI2(<8n%_H&MyU+<33yL|c5kLS+G8Q$0TA`Y_;VwD~N3iC;NHD!=( zxKuWRAm=Nxab8EWm_0Er5YyAKmF?_Qv{LU7$EZQ%H$lHqV)~ATIBRiWq}>SYib2-D zK`H_HMRihpHOntm*wu&j0D(BV{8ych*gTJBic{|&>q3{&emXRjQWErJU~I%bNo_Hf z>F=lA8G?%ZIO?FL*BN%5?=jp`y7G`ftx%I)E>UuhPPHI%-}^#g4*xW^L0}8gBNO6f zXd@;T9O|~OQ+o^uKmZ{=I)@8H6DR34%@k{f{bGP2mFZL0DI*D0E&3{*cFxu7xJVtD z>f{fo`3kcUHlRD}4&)DrAaRj3y)k%3xV{B#ypO@eFSs&UxbfG&Rn$-96{o6g^a}7? z^W2-uWRfoGSJsPSDMQx8OjK3gg6qwf@4jj~*%Ma(i&7mBD_Pm1z67pR^Z?YKp&ngf zJ(Zt_0o^V{pqny;D)ciI6G@Z;sE|FAFJ24<Ek5q%A=KV<Amt#t<O$S61|-?sX%SJ- z#gV4n%#QWkk)m+2k5J6H0_}2?<K#{3KPVRIkefLQG%<+3<R(7;X{KbkDq$9>Z4pSO z(9M4%ZC{Kmc7W1iG)uHRTbubA<g4ra8bhMF&jft*Oi}&k`P$GE3_^(vF$|~ZQFm^v zA6JYF2@QJFGh>|sP-~0nj*aZtN|gWJ&=6E>dYq*cs3mrzL&h2JBjeD~Cm*MA;BF~S z#I(|q>hXEL`2kuS*@J%|Rz(HOT$RsT8r+U6;*3;Zg3Vx>(NjuIX{o(fZm)6#y5E0) zHxW5)q}eZa#C6+IB~jGY4s($m9ie5Z?F-ruQ^xgzVa}qxJkYsVbR)?22S@ZCSaqDB zi%oEdPQGF}SpXGfse@akKp8QBC=HZxxgO6E+iCilD)${=`G5y$1)rO<7s3#xi1t;9 zJr8$@M9n7i)3v*k6C?!L+)j_qg-toG*;5F+*KvPJxLU~gwF(V<7tN(dLX(g%{Ogna zpK_^#{bspnK)KxZq#(ajSe*e)l*iE_TiFvd&me4u;LBSlaIS&tsQ#GoAn18|j3xh4 zJs<8R6-<jUtf(|QX8o)|8tJRR3V<+vcs>6Umt3-{k}^%@1}GRnYvF~@hm>+saJB0g zdzoxE3BD~ZE6Lv1jYf)TIPOcPRq79uB{#F0^ct(ZdRj&;Aze%k<N)x7^8vmifWJVr z`lF+O=4`}0s5yiG4DvWPLmiu~*vv}iiY%D}xrD6af0Y=H_X3$fjJch`RlUg%_sF#Y zt2x#tu_?Rzfhzik6KO!ei{h`p+KW?GRU$Eg-M$pd?%4ny4PSpmn3wsHLR?P}qd*2^ zcq!Upy(IFSwt-8TSE&8{9<f5WK#W2!|Kf2JX;EZQXsBaO2^;OUv!RS;uiCaF9AE!k zmbFE4YwoEs<*2{p94Z(d{+~i2yUJRJ#_<%pD2gsB1mCI)!r;Ih%M{k^8EQ(y+q(HI z)D(1zrGT=e9mJR!yo?17u1XFRG}58DFsXtc4>RX@xhfe-v7Q5+<m6woks2tG=aA3H z^@h>Rg!mC!x}Nc?SwoOijn2h<fF%Cq#rx@txFVd_GbG33csTOl>B~w0o1DNet%F0L zQ<WC^C>WsixbZnt1bq_AiLN%1%dJURGtanYpS0sPjBA72B>9CH%hJDcWguC=@=jfO zV_8YBQS%ynHp7-$Ak1OPY$?k^R6J<KesKGnm8P{+IUVfUW7=5+Zd{E^pDmh7{n;+? z392gV=bV-_pgh^Rx%tGi@cSLB?a-<{VC`FobOw1hAVnyh+6Diffhb~c_Yeqj(2T!? zT3fWGtj5PhDfgpf1o-&Awm-e5J$I3O7uAq4_)9af_C@1|iiu?0)V<ivc99-tRC3vk zzokEnjmKg>^c%S-Z4Z_K`P=(j%__8+vFt3cqd7CN8o=yNE9#G-_AiG^S*%;TE-R~X zx)Ex+Ucy`1;(8a^^+rP1Cq`}nT8>1Zm$l1Kzws@BX-=H~Y~)?=r~q=Ju6nqMBMZ>o zTox&G(UdZZS>mZYY`mycRLlZLeg+%uem18m1ZXNc0%21}AA9tRj2akQ<3NE30A?uX z&3_=`=tDJxnJd%R<SDH!blz9kn}Blxqj{fJ1JrhbpNN`(<7a7NmxBuLJE)%5od)<O zGxs22|K+dUs~8SSg!>UssP9hP2b>7cwn5g`&lO$}7fD7_WEY4_f|TY#3YA{IT`z`a zfkW%_rEeGfi!9AgDy~YCmLQ5bIS@>;lAj)mf$wy;%8UwLG*mEOBH7ny)kIbaF6aIi zU{!t%g=x|%t{M1=ydlCBhr?+`at%b1n@7q_EdSe(@0&u~TkGKvpl5k5{Nw6W$9)1+ z6~Nsgtc-e_HqnMCK~0(BQud$mxYvIVWaS<((DHGUA@B1dP?u@=z^1Z7aG$0fKF@&m zbp(l5R{{po5NSu@=2ZHpO1Ur`5Chphmg{YfL^<lL2sWtCjv0QatfH2bc?ep0HWSLS z{7uD4UY#2Dz3?1qYp;`iU3qy5oq7^#suagEBGB^K+H4K-z6bzkVMgkK9zkQkG-!?S z(q_r-mcW%>sp5k_U_(k|3SGG>&7E441Hmcb##Onw$7lAh4)Thi8Hsd6D%kXyyuj&& z;{PB{PdOb32p>O=G<!~BV{Kvi<OMG4{Ug49nXg?s?qaa?4x04=13YJ^H^^RQxu)06 zDy~ME<Ilc$LCDrvxF88Gj(;X(x@zQ0NL5{M_4$%XJC_q1TT<)0(B94q{0pcKkVzW6 zbyh-Xqm5M9Z~9se-uQX~`x<4sF{VCDqiCIEoX3m9-(gd3JZ`c*x^#%Rj(dqD7kX?P zI!~&YpJQj!E{YxvK%eTOB>f2V8xw_%QMkZDb<*)No23PsE7tiHfQx}v&$}xDcPuNA zJj|`okdupchP%nV^RkWSKi?@YGaF7viuV5hq468AsubxcMKw)~bJvs2fGCU_mqi?z z@i~b!G1#L$3LA3=!O>|Q)V<ER<`%WSPFEGuhqE~pgHZJ0_Aqc|b*($n-$9u?6!pUs z537+}L5Uw=Au8{Qf%uUOqbpzk@C!E8%{`*Kz2L%L(`Moy;?QR<U$RRm@xtEz*tf#* zIgvx;)c?)~L2w~C9={ZQFVL)z4fLot$fN$be04rdQ>~dP94Lpd1Co5-<XBrsXILe3 zI}}7JQ%VHxR-w;~RfX5uVJ``}{4YoNp71j9TRA(}ObiM5_>6O!5{TGB<1S(sGy<$) zpEq3%As-+F(m(?=#@`?0y*1H4Cgo~061C4V!8)GH3xu2@Z^21t9WoAKXzYmB0Ik*6 zpdv{11=KUqL&iSmQHRftiM3|B%uIAft$qYsP3b=2!{sGUnP*RWnEWf5(p;LpWInaZ zt1wXhRi~y?qXaME$?0~_;?qD}1m9WBG9_0v^DAq-aDlwZVf)Sm)1L1SQ8Ftbk08&E zj%*|4Df}&uj2P#r<^HE^wp9w@JFBgAoAP;ORX60hEFqJrxSc5xP&j6zOg^?NTI3ZD zLgjIvBRn;6>&R}Fl)dRtmCH3c7&lU?<*x;Tfs%tmWQ*%7k%J8^K_bSlH$>ra>LnL$ z`#p#_35krz^1~_tuOvS?SV%?cwNjm47o{_H*RR}Gvv9MlzgUpYFw=le=tM!n(ef{$ zUZ7LNsSdfvqg><zX_3^{8d#LI!j}fUlH+OM)T^DK7f{je6Vp+HJ|fZpxg#(@9Dn9O zKgu=&9#dycUpd`Yk*ktUO}Yrgs-#+9{G!5E>L<Nw{PGAKLCK*qwBr@5Ux+`5;z%!z zNrVDb5?w7%euZJa3S9xSe3$?I#Kb!RCJ~gBlV#W1%aP?3q^%J^|MS$OKXhF;ya?}W z=BYia+g?ej-;dSZ)kIib@59#~(9?^`J@tncGvhOwq$Y)j);@Ms=Q-*>&r%)DW?fs& zF==jduO~NWc6du@>wJNRr`*vT+VLD$0>(MLv5$D7%LqVgdfvD-qen**Kn%n#)p}9l z&e%sdIO8ynVm~{swzX;syh=hI;2-t_qA|rWE<@Dz-EnZmti2YEaZ4lVy;<A*H*QN5 zt+j~jZIcA2?Ve~MuUPL?7r!L8wS=P?he2~!lRwpby1#%q6t*88A%grDKbe}gQ_Rk2 zNzu*v_ivP=G`6mbKk0E>#;xo25u%ZaDf3vJo@A)~n!WqYcj=aEcw$a=YvAg|$J9iM zNmTNS=x<d_0kyhP(k~zWotEGh1fIXgw_eBE+phVQ)#|xeL2;#KswV|<KQht)59i5y zjySNhUve)zxCEdVlllRV?xv?_k&Zds`fvZV?^m3>7)Mi5RHPJQ5*O!D|HnDs1N6-l zZ1<N+cjp7>W%pj!Jw38L)PQA!B!y%Wx-VRWeEEq%k750F49;z5E-h!131%PFhley2 zcoYGyTA-i5TfLD*Q)9MnPbaBLR>XU?H-^&q@A8o~^@9Rb-_UPfSe<r6ZKY7wBGI#; zXgkcJeF5(Gg98H-!+vZ1FiU>Y41*=yphf-^nCS<>jmj0bOZk$`^~7#G2q~kRT)-vN z?wy^oW`NrOX!B75%0n$<g-ajjZk9o!B@y4Qf!iHHj|9B1S1efR)g}}Hro_yhEb<GA zt{dxe<x>*l8Qk_XWcjn>9Cw>i*$edsv!s0r?9Yz}dw}Cg4~)Yq-Mq@eDO~XB;17?% zEJSM=Kb6+5v0n}^kMpi{EMrtQgn&=OXYFB;_{^BoVOcw1V8-q*(aSAIUKFhlXf+ta z+3ohjL)X(3;(Q8pv*|TM3!u7D{1Te42a~GJN<-Kt<(GL_Zpn1rUhV~k9<MQJQ%uge z*3a!Ph`fw3x;}y8<l7sc3!Fs@oKI;9ULBw$2=0=o6u8Wp;Qlk6zcrq@HZq>*OHi!V zt@FG-bvc@V+0=Nf^dKE{kY-iv-(0zlp1u2;$eR_$FHCUBPF2`LvVv-i9iAk9*T(kb zGEi&{pvq6U4n+Ye!q(t<!l2O#%8jphZw(h~lfZ{MvECY{!;vA5c=d<x@Ig1{+9u|( z@W^E#iHCNX0L*}oAOAEh5&*5Vn8VvUFrx1M;xeivfzivADg_<C>(zZ9R-)iH_viTD zl3YusQx)d4%Q}}3&jZNo;u#Wr18RpG)M(y08pXBd+OyqhT*R~GgvPG`eo23)`n;_@ zq5;^WaUr&5Jb5lQMs+kM|75Av(PDqZmq>`;Y*k~_+3D?(d0xoy>3GO$xIyEn1tw?t z&%Mg>g+7Ul;M%P#)zRZanY-WgAI026h~zsX0#;tn#c_*t>K&o4AI@n1Sf|tlsEy|D zC%?abe4?FtzRca)Z3<;=Qc0mx>=bZ)Hy|vF|M}g@)W;P65!M0n{llA?@`}(K(WHZd zZdaQ*RQ$`321QMPT-VbTyQ9$az@J>6k-)z^shre{!|V)m2rJ;Vch9lPG8aqCd~;v4 zlR<QX)r=mjUAr{*qwEcSSea*BmE1j)K{VmiWWgkE>wF>`LU`+Z@xcvqPtKhsSn}N( z{xfl1%}%#F?WIR@+}+)&O*dpsjXR()r;NY;U{g6ZbX~mj7K~|&irqvpkpY6mIXMZ` zyXM3GstLc)<@h>E3Nij+ubXzrNoX`wwfh-){{`ZF;QG@3$1#mf#NiIo<6=IOZk1vc zqstwCa20{wq+@)uR=)~+&t&V}OIXxat`u;%)9tF)OE_@M+WYJXef7yIChH#LQZY94 zJe~e%p2d>cHdzAyFz73|yo?_`!5?g6!@BS0b=>&9?+5-$!~59?z{!Vmr?@g5F_KPu zUGJWLyPOaUCxVl&2~>Zjt7o@d+B)E6EPlIj^>$@*d@osn#jKt4gB1!s`8%vN#t6Cf zzBsBBFV(A5r)*uJ*QCYJL7Ht#{-W>FXrM#h6hRl%6Ieyry+-iVBw$+gPAti3bA+<u z_9b8Rvw5p?fln?oPI6}3R#glyr@{E$C(IWf+$*$`OU!ET{Rp_M3G8vqyHz&ctCjj| z`@gZr3Gda(%IrF*CreNY{!|3bHqc$8&3(hFc3DV8{sO=2;?~Sq8wGz5yn9zW9G%iW zm7pt_srcG5siK%$+53d~U(v6r0H{Yy?RlIlmG`#+gJZj(J|VWnBsC2{HrjtuT|%;9 zU!7ZQq~%YzsjSLPxT&tnttDC(Hhb~qhO1T3e3u}R(pd0T8gb#(y}xrjiMSAhUh>vf zgxcy^f9~zB$LaaI?$#T1SPgJ5%}iW1UhJ$X4-=S_q!2;HU}E;}BCOB*2hp|(KS*)= z<6@ygo*F)VaajCa0mfcJik*#8h#L-1_v>pLiMJZEhg__TH}zLHyC&`azcNP3@}_`z zsq!~YJRUbC_eyl7e({$mY;rs#ULDCOyduQ-=dhMw+Wnj=DR+vrvAx|-sf5^|US&cu zcbEOkk-566bmh0Zx?d9B4n)m9mgEj3j-`Dh()*7{M=j&Xm-#<-uBEyd8AH#HS?}y^ zT3ZmePTZI`UMtsit*Rcs7#j{g?Oy&g?v8kGVJ~d+?sJOTFh@qr<+qd=$6KHdS_S4Y zkpSN&>#_TOM<JNqanio|AD#XB7GFL+e^M`#yQKERT=PGbv-gpaZ<+)RpmyyRi*O&g z-BaT|03npu>}(wM)m1gF@qQsB$~|c79j>FdHXx18wAEcNTDK{(mTJ;ZKxI_g@?dX0 z)qjpT1=aJa?vV~)oKUs}3S|o@*#fw2uOwA6#rd2=@eRi%6UMan{Vg&>aX9q+btcpD zK9Gn^z@3O5Ir=xpn-aGD=zQ_pU+s>x^G}^DnHub@OTNue)+PV>JN^UKIZQH>8xs<Z z&13L&5^7WG?zweK386Rti#VLQyYf4n-WP956$-SxJP(Vu3!gu4t`)R<<5titc=&O~ zL4awQX_8i2jC<1P#HKyybw^`|E$DIe;;3oAX2hDU?D+-w9X}NcZR=|0S4K#~ZX6Ed zt}k64q=w(6=yuwjm;o<=Ti!<wcbDr3VBn;1BMV(WyxF<7EZhIJoCsc(+YCx?)aClw zwMoyj<#$;oJLPr|kma*_3mM-q>?aO)U-ZAFS$1_#Cjpdf9)JnW0^Xi=Wn5hj#2vR! z8jJ3UU&<#mlT4zd;NRT-H6_;t0H@ekJB<66t35Ti_GjeHf$GLLPrL`qjQ^B>?&Y4d zNm@@W=f_{?>B#U4wLFR|BiF<q{KIjnbEVM`+MJb2sn{uY`RGnvZ|<vv81F?z_!<9w zeZlcx*MX7sOTZYq`-X@t-8T^&{O+x1PuKPEx=D7d#?JdzTLv6y4WKouEw1|pwfBoq zWcT&BnRY7$JJ-Btxy3=8w!P#A;2)087qJj*O;uW&0YjXZka*_z+#|U!K!tlaB^Y>Z z_`d_4vk1%ITX(=tkdUZ!lLr`XR!kZVr;7>)Dd2BBw+rK&MVbd00DCj~ms)j>@POCN zS-;qpV1Q*Bp+sHja)`85nG<iy0w_TLIbis(kPATs<IsIV8d09nHs4)s3+I-C+`Nl; z>KN%&`uIJtoOv_WADzmPTyhon{r6(6uc}8i*p4=rfMr|QSOLU5(ZtSwg+W4=a~2y& z>>Q6Y3(I>*NEqPC3%WO1HuFscsqMs#sIms5!y!nHA}{bju&bv@5b&s)@;U>U++Dt~ z-XFw_&PiWSx5h`AcjCXr@1M5BR-f33-5i1D2LezOX@IaXrQx6z-F{Hud_!W6v3@6= zT>0D82^2g#J-7R4i;HxEPcp|j2LOY5Hv=aZh8*}d@VViSuWoP1PLyq>#RK%kL{;7U ztI6gS&|ezwk$cf*@a6yzsJ0FWv3|-676F^m<Uc0LFTSt>dR{#(4)trBhQokPLOV|| zvfcwurmQ*R_!_Y!Ybo!3AXrd2rtW)iQ&M1nhjR=-$D8ND_JUvkGINB8z-NF9opKug z#lnv);Fw|$+&2-(E`F<LUS68-4Ioml0L#@Ny7@hh1mI$$zFiGy(><zMRye=_7b>A| zfs47D;-|uVP+<$Wi*Ny!pNna)Lw7I0ynBv-84`quwAq=E2=fJULSccYi#$tUd@gmM zL!Z3pklw%b3}DsO2=D)%-2=sNOM(h|SO$wx=Y}>WHgOws`ZD0Mt57b3KZ-+VtohhR zeZAp&31$?P%~QGU(w(sv!U>CIV8KYYGwZs|55%W8NQX^%a@B){Qvg2BGbP0q-}#Gy ze7CTr^_(%|H>xfAhJ*jH<3Cf>XlJ|*g;gW95B(m%-6+W~@o@#58#H=XtER1$viiE{ ziZ<et@SkJg$}j;}jC0b=tv|P<Q3y#PP-m!}W)9t0(E0}K1XjUn#r)K|1O63lMw*cM z2nBH?(%f*2-&#o+brYg=+YTXjR=<DbP!8UU+UO{AXgfvOPC?IDEk4zT<dep^zXzk! zqqz#?r%8sNy9BRyl+uTVnegvU%{SOEQ94kL*|0ffM)Kb9t0g;4XdHn)ORn2fhmSRJ z26)3haOz_wsUM~+O$KWPU@r=X10=4_puUnjO`31}=U}JjX4s<|2FmZ&bcW0W#2Lk$ zkk~{WHQ*M|{`i-1MKNAsK@Ww=cIX`ISLK8Sh)B%O)<b3kG2?<4xf~y#ym(d*D_r&Z zg+ajzpw@&7#(tR3K0O~@o|&+Um$4HznyuB{c^P3aTi3(<nDAkvNjyvk#Ko;Ed!_Hv z2rOA{VNiu3-W<=5%hph#V+KBEJAiP>Ti4R6AF672R6}DB{@~mLG|94Hbb)yRi5g<o zQp{HMpvmgU*bnmLV)PjN0BFt7T=3ICI?3tV5IFWRbNo!VUSF0}@uoZ*{`(QG9ZWut zjnA8vMIRi~e~$1>_XM0f@)a5G2q|8b4wl}ENqMVDpY6aX$;B>vohoX`Gtb4GiI#@- zflU@~LdrvyuRi%`Dc^u)?o>wbIc%{l5CO2L_!6LOOsnp6F(S<r1FlRe>smU%3e+OF z2977>)!=?^WN5Dvi_cLSI-|U>vJ}cTAaAk@knCkV<^j;h%r=aSYwESW9B>1M?|ZZL z+gEtVR_NZOHh|hwAa1vR{vCV=j!-8vYOS`18m^RxC5904$z%Y!x6Iuhax!i21;?uh z=x|ydq5<pMOH<N++~Ar>yfxq4T=ceADgzT~eZ%s?NU79*RZ1r=-F);Qco!+n4p8AJ zD3Nw+`6dV$qoj^T@B|ZM6*iBvogv`KQM?_GeE)Sez@8~e!X3qV;?W-x{$pls(Y!B@ zfMJBpq0|^hcu_Ltm0%vdT}<ZLz-D%%^Zyq&gubI0DMqVXV`ln2`JcNWU<WlzH4rZJ zuOYPok|BSw*e;{zRDj_LL&2*M9<sf!2{0eJec;iA@I^)UCC__e6WeRXYFVI+&ruRu zKx>OWu;Q`Ss=!$`fwis6T-^zbs>Bx2RLJ!KD>V)5et{L*@gbmq$4vzCCsH_#pT|r^ zRKM`PW=$8wMe20GBrw2P$7Ls;USkp~r^}ha$#eV<+PrD9lj;UIIWa!bK3$)6n$$__ z>+3_3-9>=l5xr2O{6hpmKq*gxkhj;ykO<~Dt6lT$8Tx<tcoRe{>PXl!km{ln1Cx(5 zkG)!|=+smaKWlo9=Fm{ooEd*4Jvl;&-Z5rR$N3-S3U(OM@J!*D88rhN=^p^3=G4l~ zUf&qnSr7@FR+^-Rg0X9iPScjWj;X7(pL+E9v;q%R?*1<dyoPlQpup9HPWFw3YD$>@ zrSV}#5B=M}`UZH{*QI5^_HFAnHwl!par~_D2pEyaePZ5l{3gIgZ-C{7mI=&Ca79H$ z^EO;IhJ>4U!Ipc^?s;+Wk$^A$lpibs71bu|E!HyLcs>{F4PZ=-M(lO5sL8npE~#lq z@kJZ(O0!_Cyy(Iy5_^n_idxr=U3AYpZXght5zZ_1l4s?W&Sn+wh1=YqJrGluo%(x7 z>}O$ilNZ0sS+q&MiM<mjLOUbjK6?1C@U-(%&U>e{YXxbPeO~GsQwOXiP$d6_5^zWb zUhD(LlupzNkejKwRJ;CUeUY(t0KTKm^WIO1HKE|pE3f?89-UWKVt)?Y#?o~cFV2|i zHqt-7;e(ZheoZQMo0M!V^#||$5&v2m()3N70_DkzA%6(+1Mp_+D?fOreBgKl{(l5; zlq>e<M|%>Yyf|*=aD3b#o_T)iB60jKc|{}V#h=w>#>r;trzI@cg!$U8>d$_4Tp$Ug zI_)LwV5~%KpM^g09Eg@SXcv@IsO|dd!J=F{0xip4An2h)nsLke04vsR5ZTF)5}$#8 zK><LDt+ejE;1fRHkZ-}04N(!ZW~4Ogi)T4Paw8R6g17rj$y0vu?Q#DD%kf7B(r*pv z-Tbi83j2&)EOZDb?~rRCU70-yX+fTh-Sc_9|6!x!LX1z|Trt|l><ti7u7GqZ3e+4` zkoKy?F@M5#!4ykT2+<wbg<Ef<0z|<eS4FA9C~g=y7l=do%l7*3Z|Cnf6WMDd`hnS4 zIO;@YuGwpuUL}IQXjrJee7Gu(iq41FxnikYFB&?HF5gNokmQo=+6pTGC%tJ$h0jE$ zz<TmQ$)0SNFzfrjr^aE=e_q8&Fw3;$K6VvIv%3MizFP!SAQNK=I9KZQB&sNDECdC= zpVDpk>`%`D0-@E%141VG3#;sEEGFuLSAA3DuD=$6W9uOTIAWL_t@KQ}9e_`<C3R1Y z`=1Hx3t90;A)iEx&}21&4a-~)4R^mw#GVqW`^3G(n<K%Lj}J;SE=}a2vu9%+*kH_9 z9isz@->W=*naGy3#WLAceY+uDvgf(R@xHH7KI1paAl<X3UIQ;l&A=-SdbD$&oMh+3 zGhPL_A(J#JY@;uMO<deP2%P{YUP9EDPk+sHp*J=-T7d-EpFMp`n}y=j!JN=0rt$re zut+V)5P1W`{2>bL%(pE>xgd-Gk0Grl!5>SnkxrMvR*dPOCH8GYJMAW8*}v=+RylHV z1B}m^4&fW&i9J%x_Fcr|`)Gl&N&j#8sZ`)dSM*<Te0<7pT|p;X9G~QNdD*p6V(v!l z<XyG>ZVyrH3W;w^jh>E0zE8e&`#(gTbySpHxb{`L1?d!qMr!Cr&;jX`E&-A5F6kP& z8>B;!QX1)Q6s5bRL(*^0`<=7SzwdfoGd#1Oz3+Y9*M0qB2DUN6dV*O<H6JB26p(9_ zSLTAc$o~T#(JBJ#rW9yq>Hgj58o`k3SjT65wS+Ux^e}aH3EqW@UDEDb_!7vehkb)Z zlUw>sWDZ)@A;Ku%eo9LN=j*R8<bO4_42n9qkv`QZN>ij!jx|d&Q)}rvhPZ%TWf<-e z3Xr7L9Y4nyhu#eJ0gi?S5tc%K9uT&s8XZl4gALks1C;^LKk4&DQM%cXg6&m0cNieG zeZjK_s=uG7tGts4HI-)}WTq^*vAYY81b+oK5%W?>T8}Iq^^)XH!zXUsG(3>bi1}k0 zjM)DM;n_iL8AxLG&lnXZR&M}kc&Eig1fic9WIZhv+>Jk&EC^#IOpBO=^tVRJlJ4eP zoPnI~WDs}#rp4_KA~rn&cPVbkYoDDRjhlx`z%DDaq`0_zm)`lyvD|Akluh)0?$`o3 zl9dWNhlkDq>`llSW2uyC&W>?DyucKLE8l!36q@UVf5zNu-eC1ZZWF9BG6uqrvaAKh z*zse(*BL~AsZ9Er$7Cb02_&9Bb4f(8!fcU?jcwk2`R03nG25W_Nx=W+e0T3A<;|G* z9(Vqy)lp^{aQ;Lw$-K*K0hfWDQZ(sp4Ylj*YYM*ppvX*&^v0|WT~QhcheqIZvEuYl zyeXIaUvO$)=y)=+syv*Ic)&X|iO1#5Bd(tnD-#8@ar4hDJX2O}G_L$WPzwHJjN+v8 zm4Xjk%BBEeKYjW%)+tGXcZ7vWF!aq>l5Y~9%X_Y&01{z|6Ec+GCjBJ&B<mWf^nM>< zm_Z{AE{k?k)+x^Gz}hri;vsoqI2T4Q7IgtpPTS?2JOU!yhHFj%`3h*y@1OHwLw3t* zFj2qlGTv|MkS*^TmhqL_dnB1cxKT`bq&bNb=`0()ogtx9Cn~)IkJ>!r-voZ<z(kkp z1viHxC-R%~yT?Ri$uyZ^%k3lp!PkCQBt?&%h{Ec~s<@S?QpU3;bwoQ9qUBX;TsBj- z;A>CEXqfybWPOeqG1XL^fw6L#YAEU4_JMQ|4MIAi(;b{NsW9}M{2~dXUB6J&j5iG$ z;}jX~6dpq^%*Wi%9OgjSX>XPkWjL8f$zJg+$iYbbbl$zx;RR%z866Hgrc8<&5M>sF zix<pHtOhMO0~%BMG-fx~P>Co?ftoYdCrxrz=gRp_@%z^|u4MFy<=;gy!5&CE5!CM> zLX@ioRe7X>o^~WzO%sW3DC73a1ntl3ZWoN+$7Cvl(3f#%r60@UglHH-mf^x&x&cB@ z+N^&)nf_HGwJ8elkuk3)zt_p@5V=%Ngd1xFV^Z<3h>n|?o9}k{7@9z_AvHiTP@dVR zexFR?q#X68SnJ77VP320x&U>>3L-E)-h}*q#9gveQpi{%1s}~%9aZhYBh6!s8QGjY zapc6SUzEEl3d?CoI|;4o_3u}|F_R*aqWzSz0bg_mSfeK<V$bO`Ygfm8!VMv{Ssu^W zNCVR~WXO0sm#x>iQ6RX_BTDIilYT9!?a{-MzLqRP*9aU5TM;zV$T`29tn0UD)hEPd zm1P~5{T843EGN4UC0UqAFi|uNo9rDRs?rN4Nw;}jOyR>pxS9&$l$TWVRN-YlQ=#(_ z0#s%bml+k*sozqb+Fta5s>Vsgc|53_L6d0y;Iax!F2L*yo$dUtKj`f>L>@%JEWz{% zDkp6oV=m!i#Fu#bm;&IL``(AaP$6UpuAf&zCt*~?5ijNrEib2fXnfj4X+M4Bq&8vn zWPuDCK{q(aZ^E;7sE}>x0RZ6#YFp?p(e?89H*4?@=Lf&lwXWVfm4Md>1%j;L)0f`^ zNb1b@w=I~X07F|Uo`%$=OSTkZOJsr>s`|%xkL$z4bI-ez{tJPeIt0E*Q<7CA9mMZv zU~R<<HX7r;60Jrv1H@Dfh3w2-4t}^*aMSZfcQZST%keaYRq}+&2oVFug862$Q2c*) z%m40ktIlJEHh{u}53bhWTZ95qJRj!Id1Hc`mCm3B+9!=6lp=065xp2V-Qk=LFQfEy z-#$p(>=bN*IvIx^{1ezG6SizJK1|VOu1^8Ca|kMe!FH`IE!!ua+Rh#o;@xf$xGJtk zIk?yLp|+TJ2Ui{6uq=iuVsv4fPIDvZ-hpB6C4lnq0-X;C67N2hR|`DTyn6wj6<>nw z*WWOpB8wa7q`FFSCB7>%f<eb5{`nZlAk8Md(3Kr8RegiV7T7xy)-dPT#V()&w>lc3 z`}1j#Q#v+D)C$Tpcuy}OVAKX0-2y)1&byCyz<;3UivMEE>Pzi)$G>}f108ojaZ3eY zyE^U3aFGfLFPvtaxW}F0Pv$=o7@+^HxU6+;!}?oU^}{wTxSu8vKjQJXp+^^WVp3ko z^UqeRw0sl<CLhBIK-@IjZMz9;0vYVh4z>q?2mG+?juu!dUH%Fl)9F*&Y@H>?;1*v| zaTKRYyn3pqH61ZdRqA%$-Bd;k96JqFwqLlp(7L(SLco0>q_(b3+IqGM=Rc|rI6(6i zgR{Euy+ExbP2+p6I>17a4<cP9c%8RKRw~JrE+<s9u5fis1Mj(YXm^EqlRk?e2xAO0 zt{*(AMRhJGs`k4symV;&o1iSd3cD8Jr|ohW=_ZxqKpY^kn#c?As{rn0->2YK5s$5* zVv$lB#a6D@A6%<{Jy`A|r(%)15W6N^w14B2YrbBCj}56Zej=FIHrZ_7v`OqJ7n0>P z#Nq@JUH9mG1r{8F5OQ>L4(h{YdJ8DW?RQ#nN?b}aM>5`|oL?N4h`6OxJ!5>s-+`oF z+_?lVvoIRW;8KHGk4vGE{XI~Le@Rue{yaW^9M^@GLb``X!(|yWcaZBR+H8Ge_IvzM zVs>5Re6aN1NY=Y<p~mjBN>}5u?&4+_4tw1`Gk4N(brJd#&DuwZ4YBJiV#hi+@j3w) zu0h1eD`Ip)3E9HwFK5TyCu(aY(UVsV_u^o)+XN+Z3ZU8VXHXz83a!9Ri*RhLMDh`Z zzPNXI4{{$~h6O+chwn$p_022ns|o^d-!k*S6DGJHftz2-XIoHg5fFgiSku+|Vs;WN zUks#Z($;BF1j})RXMCr$7wF@xzzC7W3ITT0H~`<R@E(!pY}4>P#RCp?1k(K)V1{R$ zf1r@)KL@&JM2wHIzypF10(wd>fKrwW_-T8-Dk_b{QBS-uF){g97h;diY^rFM=u%E; zL?pcwN5C82CO0}?ipkn&9|G;xM$0MC);OA!p-E<|YLuFqo<;&?3al2f82|5!%7Bcx z85=-pj7j)vJ750f1Q@Ib)3{AHKtOcjqT9TpjJTSbnud;K`u7mV0&->1lVAk><rD~C zLkM^Y6+TF{e0Hu2h{Nd%fCKLYgm}4JabFF`>zn;**Yn_zC)~q(v(>qGK(8!c`O6iP z3vB*Nx5wRhvpBCAf#Vz&OU*8?wSlZ`h)U%p-%$lI3(vLH?229hi5J$NuzlScv1zgx zAYPihj=*{HKkK3Ms#lhlB4-r=;{z<8(0KY+dKPar7j>ogKB#lSgTXiie{b|XxUuSr zspTselRSV;YY<fa90<HRB|t~if*VIU2HKgBwzVn9A`;#^0ZL_AD(eH#W@Dj#cOII8 z0DGsb$*iIxezOD7B;^{u0mP60pvQnNs3H!?UIyuu&M#t2yn*R%<_|~z*5+pl44qtD zsKXuUF0|CtqqJ^ab~jZ_pDUMABrg?yT&yY<Hk?+8KNV#H&vRf|j?&;w@?VZLhpDM4 zp@e2y61wRtDra7P{@nKN|5g-ymc>p)BbtOo4*<?7eNX`cPfeXkAz%BLlao`K<Qk!8 z4>?A(cwJas1H{pcPh@-o5Y6?Hv6E2D03s)qXG~B!IR@P1^~O!rgdx-gu;>IZu4v%o z<aFNZf4^@e{A3DX&DwHJdf1sssh9>=R0ieX@sjOm?NO?D7m33+Rr*Jy;4p(cehCQa zre`V|8iko??k$<vM}1Y5w$B=qN5{wU0HF%<3+sr2kMI;WTdDGP*46V}cGNp`eY(A` zW40z3?gIgAuLhfoMoc?dCh{RMf^_G(|FUQt6=vhSlwX`+KeW~!Q54gqn`ltAA0Uu| zrFnVJlQJ_aIHs;e0Ir7pTj~oPf}!DIEFxm!J);%vZmzEX*yoy05<P+P``#6A<Oneu zp*Y2*x<CJ|dTxC@gHOJ@Rg1PDV*)?y<&?RCY$#9G_ccXnVfjlBeX6<-rZQx|IYnBB zyoG8liaJ~<e{)hAS|JCiO18F`1P=I=0#cOyy=u(V_|r|_*;KN=8{@ShStn3bZvtKp zaS8fPDevmWIFQb)Nhu(+1ElGF0sUame5U9Wbngp+biFsx%|h5aI|sH}bI@krb7}(6 z94|#!K3T8bY*lt4xFax0$83x9*0nk#DZJHYPKmc6jy89}<5{ROXeq}IqYsToM=(QS z$&x5OKrOGxTmdki(%+;iogh^v)d`d#g8-eCpLoEHFO6?vOR)8o>ng;P3*_hgbcQP` zq(|Qy1B{h6z8fNrr!@{avJ=_oODejaJ8t9#Lb%6aT9EDlTsNZSy2|^3TR9ZyG*<$_ zDX}>&uBa%l#fJma!$`VgkQn_2V8qV_@)Yv)^By@+%1NrK#<OC4Oz6B5X+?EAT+l(x zlST$<(W#K`Fn5Z&fvZUG5ZaZrWg4I}*#O&e`L8d^f>(voA!nk@6#95F5O%$<Gtj3p zbPHkD>bP{$tB+z{Vh95$(+pLMNe>d(MMLrV2uU<1+JBX*m&CmNqA~TN5EcTLS7Y^M zY*cr&0~*@i8>}Op^QTV=NIiDn`4Vh3A^;)=*wgch;{rHnUQbSjLQYLIxQX}<F9a}O z(c=#Vn;v}Bos#d9WfndEvUEW@*yZ^Ihv8?A5ZKcMDpfRGj5N7e{TLzKo%7V@DAE2y zRQy@?BLK=EBgdqiLrF>27fZUb$qBGMGUy*x3N$9Zl&p>oJjU@OL>&ToLno>D;b5aN zs8$r}qZB2-W5!y@U1zCwK<+|E#YDwKjzk`M9ErU&<?y^~2X&iAt&=Cu$AekAGZwDm zrp6ybDWpb3cSy5~nyrd+*zF^(Uwvj6od3~5)SrdT9V7v-fV$8RaCiAJ&c9AbOPeUa z+%@P@W3E-smtR%L|MO2ReA;+ZA5$eyZH2s@sUL)%TCm!%8b*@+B+u{aMGhG920Dv6 z$@jELl_p@xxz_bG1dB*FJwfXOJr`My=KLU%#!Tf*OkfuuyHStq>*)Kb+Nn~{1X*Hv z0UX?sOu6i!ZRRPQ-auGlvHI()7uFI9IAV$TR}%xpKHs??E%kpC4vxo6LLWlMTb79S zg}uCvURxP{0%ItZA;o7HnPoV6g!@XxPlZcGSF{JoF$f9;(Aj9VrFg;om<=&9A(Jx* zO<FOv|J_OLdW=Z<RQCx`O~IWYBA-@SItG|PkT746-vW^__7aRt2&!Uvhen)421jY> zS4}x<kYn;2VK|6YDVivcV-F!kE3!T@TL=neibVAn>KkrE?Z7B-3U6f`Le0cGJI2MP zvIl5UU9w4@F`g-k*ZK0aaJp_DuSQ&zV>$tTi47h?<s^LAi&EL@rY&;UD|s(dr(X2F z-xj%_`xE_uUhu7$L2P}4bIV;H$n<k7D?U?GQ_e^lzZyCDmh*#Obn&vxvyivF>U&D< zsE~~(J^`a$rm_rEE7kB&#wlHCCmMu%1PF)NC!J=BLfsCuJx>Rj_~6m@IZl~g0n08> zE^^qoC<LSZ<iNjfOtrSN^=Y-SO21mHX;tse2{_iu3q&NThC~Ts3Sv<99EF`oG4k!` zIm$AltqSLcphHd_T>??3n)mmLow6#-F0#7h_PkpGUEzb)m2HwiF%8c@z@7EY_>BeA z4d0VH5-Ob+9%lwd1x5}9SUci|h*BgYwM}V|^WN-)<R5qCB+DUBlY~Y)UJSMEMkB-$ z*XVH2rqiAc-I*y%%$QN}m|_EAK!OeH#m5_-r7j;{onjLfQ~x{AfQfQOVxzFMiBXPw zigKO~7}Bz`EQRE_ygk_xQ&IEKlCY9mm47+f&RH0(h_xF+_QWzM0tbs0ms(W&Pnl&N zS4Yr~UxOWmY`fj8lN%Qxt6q{#`QYL9>Zp1*^pjQMkbF&NpP$)g-56n6z2!{veN6cy z?2HMmuZPgrZyS%!%1UxDGhv?m9?H!BzdYw78q#mKXdeevfDxiobpM{EgvI(={JM(8 zpDf;ZBWgLRBWXJgLN;0BuqyT0C_vC$j8XU<aI3yIONyA8ZGRT~_kA|=GQLc9#9(>+ zFBLbuUCP5R>Cnndmn>e7>_KaAU4uROE{sjMBv}B*M`iq_=D`*5GnW5vS5ROWY9ozs z@zX1nQl8)NHyPO6M_{yQF1%oJ%R5x{xnK-C0VYSSx|!f?vC2ASm2__>w<r>nrqAdy zzQ&P#OuDbeX60!ADq!8yK8cl;Ckfw}Ta-Md2PKk-i0HlWN^YX}F-1{m^s*uCV@!i2 z`XsEk7sWE+9_vla!A;_kQA7io#8dMnL?_W0ouI{CB8Rps%gGc)(heZfUN43q#X@OD zHP7FH?nwg)p*@VhxyEwkwi`DqUa6fzm^h6YP<5Gc;XY@dYrn67aZK8Ox6A+PeVB|? zNWPeSbp~yZGM&O|9&~|n;yu_@pi{ip=y)t7+s6WOCM}37WWE84l8Q}w;>RNl5jGIg zngX2bm#gV!24Pha<n!E8hb-b%ax#>SKbd#+Of1IxOfg}U0c+o3CX%)J$Y1^;kmv=Z z%;+D^r+8ikksm#4KZcd<0779-3t)NgZ0M0&4`Pc6F~5UXfgy8Fa{m{8+w0FrHD6ku zv`JESd-iZ&e(vG<T6J*S_#PQ`bL5v#o&tikUT(uf0D7DjgP<Q<;5I5bH?i_^u+XWf zCP;vd(Suy`V@08^8zntf&?12gl@*8iGfG(2ZsE&(m1+}QufY>?8L^%Z^q44&&cz$E zlpkcc`lE%Tn`3ZL;-LiYt%$CFhWcdH_o(FPjec1(NPFvYXTAwt@<--HmuE|Cm4UAk zG!HOEjoZAPs>`p2EHGg>C<30~pq)a+7sE@7xC6E5-b0Lu3g(Bd8a<1ifE*h8z90!( zOcw=z6;^n3kX)i5lOie3OBPd&@En@f&|?bqI@vQeSmYPi-fpz_ZwqI+`zZ^CWJMK) z;TC_z4Sg>(|C4vtu`u}xR2G|E)rBJ0fX-i$IJEntyD~N&S3Y+r=$fAGhdQoFy9anh zESCfbGC>qnX_r!l>BB3kSN)h<RdBkgTi@NH)QpIClrLrGz=&b-AG|rLkuao;$TZQZ zc4H1k!^hD>PpJdqaQH7DhkWW7sCyaIQBL%vswDLa6X>xRIMM1hL+R&3_T-h=Mrw}J zf<^9GhR9w|x%y3VX%)A))2y5Kg6g~OBD+Z4BR`g*pGoBS-R!R6<4r}nyfX`mh$v)f zlji-v-2&xjXAWRqT*A$o#LXhf(_gVgd5zQ?cy4@%Dkl=ju$->StwB2ykni3s6H9#x zZdtl`u1|oXQ9v}33HiM1@iLy2%lKo8y_ol<!qDJs1o{PWHt)##tKZ*|lf7d2(f6si zZ@fEENJ`0S@KNF+*gBp1U!jE9TQcxHiu~{$Zq1$ImU5yLC(w_}$V*)Tz@3TfXV{8U zD(1m~ePuLC#rZ+mtYTZi-1Q1C?RwC#c}&N)wi6K{Fnf;&=bNWIdXvI(K-k)wc`)2K zbnsIE2`-xd-LyTxX@K6J5^sVFwkt_y1{H#QY@8DNQ$Nj16@76Hu3x+{q?;|k_rVv- z6KFwC?877-iamK0TpK;c31mqs*s$Y@2|d4yPpvj<wpFFxtND<4lNT&$@<Fo_bF6)# zc`s?wB$BNTc$5n|L@7pE(3zz%SQ0-d7SbJ!+ttrj1I5Y)RfYV6%G9sIkHvZKbEzLc z(3NcCU1goWpDuk__Qf!-r(3xkGP+A!!rd1vvV~?`t&m~5)wh6)c?jB7I}W@B&wsMD z?&hr(uJ-6tc&-0c+HFE2lw^!FbqX<n60y9u_iI<ImFDEfL_O)Fi-3=iMLfol1>YTQ ztBiXaPhBd>9>(S%4QEM;_n8oq^BgSg`(o1Vf}A!9Ot?a?#st@~vzOm<ILxYcsjHg> ziJuB!c9pi9dpMVS2`pxIM-g_<_c4NL6O?=~ZV{5>wO2L)bbzOOAO~=O1vek0uY@5C z-v~2~G)UmU0ar%FSZ5k5hvIkzK?cYM;phYyFOmjjrrrQcdWM8<gH@4dFH6>qd#u<c zx-Y1{GqeQzf7Le*@$h;auT1dI+Y~Gxf*uK_6|f%the=0R;ySDyhCV$g!m_%GPENsX zJOn5@L^og^N`W?Nz)ZLWD4d^p0q<Vq@bi*a!3g8W1UO<zGC%{>LE{q-V-kZ7pvB}r z`)RAgLI2$QL#cI2B7P20FhOyD)93%F^5pAd7w_C9KYW<2%Fjn9Kp&Fa`vN+Jd>cth zpIexj^uF0qd4a?wY2bgtj!|%v_0LVEI}O>QIDPxZ^W?41^Xthlt=Hm_t%k3bejb+( zmVnTQki609XOY*>u!2l7<&Ky##0m(~3|7W-b7&-3=DFm8+SuAQ;WozGcc5Re572F5 zq#4Cw=yk}a`0t(1$;mQNTO&@g-#tikia8g`qWyFw6LiR{>JRWl=g1~=lq*(^Mod#P zjjnfP{75DH&AmoJ>}Z;l+&UJ{0wGk`Dq89~U~gI&cn7PsQ#-s7n}8G<)r@)#zG>=x z&yjs-Fbf$KVqpBqNDcSk{&$=p&oLR#`SVK`D^e<uR#o1T^5wF0qYmsWbqn~3hnAD| z!CuCWf$KdLpwT93P`IEten2$fo1}F4JiMe6DfThuw6mu2#UjBTfKBDG5|9r7ozb~s zR|`D6ph6w>2aY^<hx(c_4`8-(Id=qo)_kDe@B(H%l_DBUV5e7^7=i2(OKNRVud45# z4>tnV#4>X*|H3Y<l#lCrv?S&N_{*qMW(@lF$p^o2F3So6n^_dh>>a*XhBO>z9poEG z8zh!afi<SN1I!)huKPdPH2|Ji1sn!a;KDf_?@_?GKBuJ<?<wM@(D@6Ul)wkL3iV%3 zH#EjOf?%l%HIw%*;)Ai4U2lS-stJZ(71pY#c`|b(^_wM&mb_Zc(KpUOSj0XU7C3Gv zM*~6hgI{63v5gndU#OZ_0<r4z70I#5p!DZI_3T3cH2CWTh*=3NAV>r^1s#flBu@mR z_o-D6B<(%5&vB=2s7k7V@5PU~e3M2mYcYmTbSMxtG98&l=NH$)y`oJ`P5HNnEu1bx zEG4C%I^BTy73>Gd&@1Q=RZ!8Rs=NU0>{=B$xyVc791tjW^BBO>EC5aYjT}ik1#9VN zCT89D<2h7b;J-?PORm8O@aS=iN=m{2SCvE<oZJmh1^C5AUU~rWMGCa-&~0mai8CzH z?Onl(8FqSh7F0lh1RM0I{&(B)ZyKL3=><d_F$`F|33g~wIYmXBZ19P~{lmw>`=6Xd z`s8NxOm00nVP(u!1?SpY77UIhY1_$KhLfr|b4o7Zs&3>FQ7oj%E4?H}*i=9#%m>Ck zYe--Y^#~mhQUW3HT1S+I3uId-2U5u(dk~>8XPo!6zi=;<!tRS`Gnsa?i{%bb0o67G zPxFDEmk&IJZ07)(91P|^h$)FWj4|aPGnpYEK$#PSbN3;#*36N18-ZIaY{}<Lkqttp zozXI5TWx7jSlmWtV?i=2FYn*MQ$HTyPCTpU)r!T&|HrkKMqfMaU?Lk$Zs)-LzP;n2 z`5uJY;$)e*9Q;yc3SRcw|BNw<ZyF4K0Zj3`W9dIseW6cpXAn$OZ?;iWxq5Boi7n)6 zYM=+V#TeUwMIijCX4cRfVX@?Hh~nd?O2PmsLW!3%zF?d)AJ{1j#7-z3d+_rS4Em~J zIN1I4Wd@j$fISdq9oI*jru6{2#`y>#aX2K5+DlgK5gEg^<)Lyu_4omS@H`7qwb1Ll zejYKGv&fg42hOo(@7H=FbUEG`4t+p9n<v1WW7~8|NqIiE&MH-n&-@KP(_SXoJ9cVy z-+(F3(u0huWitdVEl~9_K#<@rf~t1U31@Q+2)aKF*MPZm1>ICS7;8px_L*4{UNnYR zI*rfqOHJN^e!>kSpOcU+ThnRjBTD}M4fBQh2Ovp0YWZ;m+=7{=$tH8#`L8%Ql!6mO z-vvV^6|>@0a@#<m{IXP)SYp&yaOU#hYreq~^XHW=!9*bdyDpUvB;>He)-xq4TbWhG z((9m2zCHP8e;kQafAZ0F54FfdI0y&IvtW(*;t0FQyI`XwxwO{-Pe#(?XZ1BWX7`xn z=3-vw==VT24VWp#UX^m4K1U{->>lU^Luxb1p1Qgj4}3@OGjqDSx+XyVjSmcv*)B`O z`!KF2H=vmTG9TXueJcg}l4VZQ&ZnpuJBjpJ06NpQz(7N04!2>3_l2HB29%ToraHp7 z6;oUAVa1YlX`;`40>u9`qU+gKN?@*Z`*gR$*N3p@ADOd0#oyYPWG20<nh-85bd6x8 zA+jXFRp*imfcb*+%4*GRZ{`ia+t8EN)RlP!Dh!s2zIgQJVaaKjy`g`nX*&mtzOT4P zVCbY!>UUR{IWsjJ`a{EmUx9!-;++y6^$`3o@)`f>B52Y*L)fiA1A-4tq1fx{@UwGk z+7Y=xl3iiyMT^F|J`urk8s~cBySn3WeB^tBN{`C<^9+V6e92$U6jhvI1gnNMWyUeq zCnuD8eHU`z0*0KHIPquz1W!-_Yp_*?l?IL>_p!!ByQ)wcjl=iHXgk4XcZ-=<X`@I) zOwQ0!Qc|kV5nQzQz;p96lv(`1Q%Rj;jYQO+8*@Bdl+i0xRXtxlsC7sdcGuW}D=;#< zKf+n9iWgsd>c^(v5{TL%Gp=WJ&8!y@kq37e$@ytudhdNZnSq0C$XslU%oXgjlOMK6 z-<N6EJlIN3ih*Ysa2yFUdAVfa(*5ha6?ltjg}Lm2LN)<%z6fqWVTknRB4`&IEBKSf zIdB0hzCPhH>tRZR)xO2e!5d{L#m56EZ)>t1W=`tAYk>okA>i5|$G<eJ!g}t$!EH0i zN?Jq|j^10M&{WXs5ZkWBaveGrk@wX*ppXeD#=x<`>qyjwuneyeV!x4>$0`J!%X+&- z-3M@?8v^nbdzA?bB2@`ceVOCPGcmP1hY;j<K$1(ya$oXv8s(b}uh2lUyjUevXadsV zCg^m<ft(UKJG&}`w7!-!0k-)ZW<aISmkt|3bTGg_E1?94<A5G6D-J4QmpDW!2{ET( z<>m%ZPm$i>6mL5S?=SMhYGLq%nwQ7IeM2908_Z<44~`vwOZT!6Y{oHEolxt?+O}dJ ztOWMs#_1lC7kzuYzx*Pv<lL1!bSgj5B|ZY`Dfi`QFo*tGvs+3`=b_hmt8S<p`+6%g zNF`VYn|VAUObFp?3=XC}`=sLYcQp(w)^7vgxMM^}$UbIP-M?fJg&vMRISugn7V*CV z&6X!+HDw=`TATPEA!3$5ftJnc1wt4w3Ms}bb?P=|)RmJ{4ZT><StwY)fPlcasMw^> z5w92m{|+GP?7+j70{oaNY+MEJ&%l!%XumzIxX_|^$wHU~Yr!5r433o-%RXn(l&&+X zP?N6U74+`X^a)&qFKPqKe*RQi&KOBDSu9Mk-&9UN$T%_2Th)PT^P!ZmEr`et-C>HH zr}!Dj48)ndjPe`e+I?#+scpjY_MMzvxA$JWIoDfQ^bTvbrnltpwC6#G(K9d*Ax{e_ zwC-5?q+Eol;86Xp)_f|MV~XYOS8~6Yv6{mOcYi5U(zKF@V%savWB3~D<OIU~B|V_v zC$^pCJfa>Mukk{smfi()q$?s;bS$!gLpzfoOJ+qX4PyqpbzvE(8YoGomCP{_ooU>g zcxI-kkz^Vm80?%3NW%q(QRz08G3;w$Kvv#;<+Fv0=*0ELAc3l!#3`~eqj5DMrAY!L z*i>*ld?4kc`sviAk=OvWy8M*08u6RA<oC}}#p6{I=TEX{r7yHNsx#>gP6h4f2*vp^ z&zPB~>#goR2^bXX=@=8yUU_w6+MB0IX?DG01&9V#rGuMr-USXXl(jf%T#SYflTunK zX7BKG3LHj(!2|UyE)9LI*>!&c2>F33rs`e6R>ZH&qN#?mqiuiHrO_B_@9@6#j0m=N z#`761@W>128qDou*)K>EUkb-lVQvTSvkLG8@h+bD$dV5AfLp;cuk%S6zAR7D%5!_t zlSferQz!CE(YYAQFnTbe|ER4Vp%d`uRdqagX|nF@ihyq1jDX-078u1MqqHR*><8lz z1vv4DiW8lipAQj~Jc&fS*1fJ=iCDpVUZh1Pe~(wm`OSxtCjOqRyVkEE2#=~bgim2F z91^|3f(_wl+DKS4w1@bmbD2ne*v@wUdEN$Ej(if=IkJ0A!MZ&ovY4d4rbYI!E%@I8 z;(v`#m@OUTLgy-)BL&bgg4l0>o#HuA?2{0om13ue!2=NP_Ed|d;`997^%(;L!)vDg zW>|nyAj08q2Ik6yxiR##FPp+DCd>imSWTxthRS@Z@C~c!8o}3}pf9C$Ydw|;Z1oT3 zf$Yu_jSs*Y*Q@#T6xhDxN|dtY2EJ4;fqtUvyTkwTib*(T+(A<|1fj*7BT5h#&~jk1 zYsQqZt=JyRZpndPfi&Cr_IXgfem;sG<+xRFamOKU6F52r;~M{YCqc4U;>d`?VjLnw zg|*wx2~10R#jp{c8^rs9%Cxr@0ecsV=m0YyyPk*@EG{svTJe@$r{-OVV2HAL;bX^_ z8F7gITLQoB+>VF?HFP#Fp{q-3aBy(a#Y}A-(M~yu`Yd7@AO^LR&+;U@&FaoVn)(Y` zZ|c7h1>NkbVUN)G1rQ&HL7VmS?%;hYV6l&@>-IS(KD448-GH{-u1MUnhtXQpl?=b< zso8H((rCi`o{Q;sE9)Z}b%*mj5L^`+czHGVeYOrRhevb{gtvgjYUOooSV*%SpVJeo zjCXp9aOFWy*TD6)2w2p-3{DB5TS&m;GYDq2IDvPdW6Scex5c*RwO;Bh@viqX_g0GQ zfbG@m!LU}5>xpfF7>HPe{NWq@6Q3S#$JgS<hJ?_=ULFs!>oAvfDxkkBC!bLEKpbB; z)2<5*j1PF=9uD83o>Duu0@<FpAL7`1=1gfa5H3TbwL)%69_!;i;G-YU@8f^Bl?m$; z81jvfKE-w%`8L9HeHP(=O6xdsV%TT2mg``%;raf-&}H)V&0=CTq(gD-<5Bi$pvPu& z5B_xsZ{uEh1#gx2`A+`VE2aE6kn{N}H^B2fXZznzTmH}S{w)JjOP@J#rSdL%3Ty#9 zXM}$a9@)5*&*R;Jwx;4#{Ba*9ANfMD?RZWw$W{1^;z-)-0Q9+UD0|p;<vnHnRx=D? zLa*D8muW0F&3kAQC$9VMz6HXMQX99gJ@MKx)=<(pO$T$6tP4~B#l*$uY#6oEPNnm* zv#)zCB%aH_3>m3d@H}x(iFRpN3P#qiS#o79azdH`b`x|seCWmratpbkRnj^6F|_yb z*FN3%5f*D<r@^8Mt)~Vi)qdcHvv*I|2|Dd0hNa8IN77l>K106Y&km&*oY<#AdDlJs z62FQvdG>6d47kwY43hXr4>0F?zMYEj85N6j^qq?EptpTf0{Zxkjp?7w2`x$djwr7b zc5kSA9(!iDj@HGYy}{~YMjJ4l;ck^!Mmh=<RfGA_<20dJDz|!)?e?}f<C^n$2Hz<e z$X+YV#kGr`)tV1$Z0$2nB{fI5Zx~$LdAM^zjGhUI@<8_(jY{G#H~iOr4ww4N8m)bF z*zl?Fk)LrN2)8(VeWSAVG<Sfx?iUko1XxYgCw_&H+5FZ`oi_LtR_Y(U4|k6|qkLoy zFU&<ev+AX`&N5Qt`y&K$(tTbVZgQRsd!LG_cAR1-O%KS*<fNWIr*4HqKZ^E>FVh{# zE{OpUR*_MM-_0GBLHo^Quf)aUT}6;YB-(#j<)e}A9s2svHx7TfFkTg>QHtBr0mWno z{K{;HKOz>O`vT~EcE4FQ<}HWyY+62tHXKdR(wAB0!`O90$@%T^%d=C8#exqZxJ`J{ z+b+lRzWTzfQsek;RfpXwdTv+v6(hdCSe-+nbNaW$``(c;VjA`}4eXe_+QMvjm(KQR zHnAzTP8)Vqn0j&tUW*(uvhjWM1Bf$93AI)V_N@%(`hA?EzZuA^&xc!Vxm%`J*hHE4 z;6b%M?7D`Fd_6VGih_e;PYhZW2KQL`9q(vlCJGssZawvuh5~%+?CO2Iqc11KuMIA^ z-4r(|y#oSIpgDuXves*PbY!cBusJWXqhaS)L;4{a#qib*u!<eK#5|SFT7y#YQ^7d& zhM(v&3dec7C<nh^cJrSd1xV&TDekLyS;>_u*<RTkx);Jtr`4>iNl!LesqYnAooxL~ zyYR15Ev}$B=^YQEW~~-F(45iXJ09=!aeEta37MR2^9R8#{gYXm;jObAiE26>vn);e zGtZyHcSijGu>G8g>K+){eA-`7xwluY3Rn&pb<~WzQ<1?kWDcFH{89ZSPVuiZmLIa) z;QNjSYm5Easn_<8(_>9J&z4D!*4F!nJy1`XpfnPOFzPjt4Y6x}Fi(wfwBBLTm33O` zF9bfveSduYVIj)<rtu7#TjKFHC!Nb*5j<*N{oAu}CbJ$q$ETh-vC>8{$MYo(<I_HM zK4J0DT>CiH;^P*VoVDxwZ|mk8I7^w!Mc>~3`G(EMu()vX^>Bt|LpM18vvBCY_szE| zSE{qNziUio_X>lv2nbFn4-Hw$^5+iy*!4oD{W5L4T+O7JDhyg;5}RtntsB3979H## zzuiKQSOFX7f>;LH?tG^~>iKG$ho9d!hYM>jT`xz`r&>ME23M(9w0_gm)4YEMya&S7 zr>L7^EiQd;Jk!T3hz6PK2A}nO8+R9{N#yk8FzXS5=4uTp51`A$jBIOiu6v0`x5010 zfH*ipTs>e*j?z%0WqdS4nBfa~@Y<SDZkxwnq@?0+8}{7~&Ktsn<NVv;9t;m2G@KBF zc>F{2!f&b9e^>Q$Q=5zgC4%>T#?F@C7+l$0WHws3o!Wj~7QZ{tv!#+7tfVO4?#hfa zJS6XhUitpqnmhDV24Y&A`c}}}yc!|EbH%P-tpGKBj?Z6{-`_NyuPu7z(!V-2fFO*b z`m#)x>yX!KPj%VSmDD*MY7+K_wtTJx+TOCiDO1;b(Elpsr_-u{EB@Dbce*6@@`Hn- z*SiI@d@=2F%Es8&4ZdB!t7W&u)qMHAh|oFZ`^2wU*&*<=kNa=w*9G)=w`<0?#NB-D zyo~?;IRdHuo&$}jCIf<@-VVO}X7SsvhyF&xmsgIq&20|sW&6bbj@BhHtW*BOH6Og= z`+eoOdbGU`$I;;h|KqQ}qxbiG_jO6_Xsd>Od~I;whm@3>@gp$Cx@|_&=x>&BZB4M1 zn@mkB`lhIOHZlA1fYFs_U;fVw;o$hF<DXdUDn{!?o}cA^rfLQjCB)jaQ`TZKdDiW( z*Gn*jpGe`6&Tgms_#Dq_n$1z{vOe`i&2g26w)1E?T8&dJKZDX}Wr45>{UXgfL&L4= zT~;1nE&i3c$sr#~sf~Mn`m^x??z^pwR-~hT@m-nmW1!E~qaA)k!kt)pxs-afMViyw zAp)L;=fm7NGa?kC^I5pd(UO6885zD)v{D-r1z2f<%l+~je1p$|&ngFYTsJ+s!ksyt zqjCN;TtXjioPZUNbatz~hYjjw-b4&t3!|RXHvfLHyLt(YG4H5{TdZU^;$8=BuakqP z*YJ0kyJ%b*b_anN#Kq(G(45FNJ1Uy>grwqYJ&4Xq!sTFT%JxgWe6pH-Vm=Qm_8Xo( zoff&i#MHi)+D6U1)^zy|9QJ2M?D4!;)J=eU*KN>&&Sa8tv`BL$Quo$YXJhHeziwOp z)=f|L?Z0VWj<fLCq~lwBytN0T{i^h>_Uk}Cl!S}~#*ivemGpvv%@CI>!)p|dqej!8 zjb~Y1&;1p~QnrmerDIRd1vgYIwL1b@&&EohzuGu(9&;Z5m+A9w@msU9em$1g6qf$N zRE6SompA0hakYcx*fDa!-`{X+IsymmL@R<zLB4(v2xlA#Wc&;|HJ-bj+iR4#tRJ;3 zE+#(=rl_~6<G}(jB)-lnGA|kjW7^5k!|^`*L%kIES=_aK&u}Bht;$X7DR_em%s5-m z6M`26+&)^D{BU+OnBQFI<3K8IsZ7~Eb)<6dNSZs$;gBe9=He{Sc<|WL*V&qLeeqKA zx0Q_{$Q62i_HY|YvALU%H^Aq8KtJEy<^Iwxr@q=qnY^&6tH*qTj)ba=!<po?2H9VI zVHuUfwBvqUq3J$sc|+pgb*#Q)FHxr^4#Y99roNY0Pxij=v)OlGZ%8EmY!I^APW729 zYv@mVY@B+RbyMiTTtn5nJV(L-=>E^tqjn!7>K4<u1()?`r^Sm|>!2{qFu7p4ppJIc zZ8jn!OI!Ge0BIY1`~2!+d-5r|#6RJ5343YiA&R+yvoy3T|D5l4iPRS77AiAC8bt%p z8-`hzqPJJc<7YQKIDyw5BTeYbRwoFKU<2A>t|)LC_iowuU~J#sI=0nT>Ew(ESiOtC z?)YxMd|b*(X40-IMS5k?0fjygC>z2=9eg~&`x6hH>fG*?B^y`tsDxI@x5C)gIU^PS z)s3gj!eWvNK&nNM+x#HaemlwOH}5dQZ|ah|jz*_(Sdc9CX#?w}J$<43pz+N-K#@kH zX@qqQS1Wlx4{Cu3PEhSCJM~AYzvxLu!-U2T(T!P8sEGTy*Hg}IgCadvY_ooPJb`gE zV@TZlXdSnVeZAwZi-pcwBj8|i-B_poL(Sd!G+qYrpSgxqn)ZKhLl?a24EHLk+oyc& z==swWsJs^(p=Ysgpq2f8Q#GncI_q<m^}{|Q(MOLMOrviV^+tz(<=jE^40HMQ#IK!N z4;r>}IyIw5UxO4ZjRTT@`$nf@ARMRTYKJbY!5)I5gjD>c=on>4id{nf-Zm+P^++L; zA*kb8$5y|_oge-xsBaHkl-MuGza`1r`@G6%u-Rw*>wqLjQ9oCUK4A}87}&IL^TOF| zIa%N6q(B?^{B40wK#&0`<Tc*KcIu5D6?A+n?lrY~4!ncc&ysn{qwposs3-MUfn&s| zKsF6CIsCcU-Ep^7rec$fG%tYClPG-kG`kBaPssP+a!Pbv4LJoj)+vh$_KmqVH#fYe zexb(#6plG9s9IAcF8>M}Lm$=_M1|Z&n4G#Ie8FsUF1`-cL+CKQPmOU2VUf8~=U?NQ zd8e5Zi-8*7*wnCpB2qxL**g^ts~XNVTrH0dT)QmG+%K6;(So(JAdN}L$jE^UC>xju zP<AkZsOBq(fM0B5_GEo?Qo31gn-PbhpQXjDV(nn_!HPNtecj$SRr%rmLiu#{;QACm zxaGf9ZF1UL*pMe!G1z+*?$9b!7|2hlTeZM-nJX4Ux8#*^^-Q=TYnGNJ?MNfA#q#2D z;_0q@jR`dGzLh#GzD==FY_!m!K*J>;w$cAq+3|z+Z2o_rAp9JFOQ26#*p56nO-*|~ zI+I-+n<W!<>4?TEJ!Q<A(l(9S$^d887RVMUH@A_Tuc%SiCrdBUN)@&-Xd|vV^J#l4 zwZ-4J@wQUGmn%Pbq3_CI>n#QugbLt)t?Ul!(S6%N(7|wrXSp^B@{y$97S-wEx9>(n zsXwl*&2s^Cez>|>m)dKG_BZe;dc1~o(sFJzeb#Kle&c!_io5FYaB(e|Wh5VVk{TZq zYFVZM6J||OIHo$e*AxKg2o=xu`^PvQI!okbf5KeZ-YCincK=GMbVc!8&3N)hvl6=@ z>l8Olkt~x07NuMNfwnsgFBO1Y^u=UX$zuB`ve{rx-vu&BMx@g>PlIDLG)_erzs*!= z2`upGYc`^;B@_+QGw>;b>fov6&#cKP*Uey7sNFPf%p*;;g-|t+hN?g&;#S{!eku=j zKWqicV>~UVak)4flk!{Q;89os)5e@f1AVW#FZseCvNOxQe*GSO86mcfdJB!i#~EeC z<gN7G-uJ4>MYSOfCOyvx*G9@@$I+F2_}9?LZIYZDTka9ykN><JM%;lW^k_bHI23%v zpAtT2CFB*$1iT;@%z6Ls4h8FiToX2_YdMjZqXd&lH+J%<ceQMu_5i^;TGyBR)#m5k z1wVe2#s4+F85HgKO13LZuA0tI;fYm*9ERk=!SoD%Hxmq%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
* 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 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
* [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).