mem_mgmt: Add a memory attributes memory allocator

Using this new library it is now possible to leverage the memory
attribute property 'zephyr,memory-attr' to define and create a set of
memory heaps from which the user can allocate memory from with certain
attributes / capabilities.

When the CONFIG_MEM_ATTR_HEAP option is set, every region marked with
one of the memory attributes listed in
include/zephyr/dt-bindings/memory-attr/memory-attr-sw.h is added to a
pool of memory heaps used for dynamic allocation of memory buffers with
certain attributes.

Signed-off-by: Carlo Caione <ccaione@baylibre.com>
This commit is contained in:
Carlo Caione 2023-09-23 17:34:25 +02:00 committed by Carles Cufí
parent a8d56c4f1f
commit 09fd6b6ea5
5 changed files with 269 additions and 0 deletions

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_MEM_ATTR_SW_H_
#define ZEPHYR_INCLUDE_DT_BINDINGS_MEM_ATTR_SW_H_
#include <zephyr/sys/util_macro.h>
#include <zephyr/dt-bindings/memory-attr/memory-attr.h>
/*
* Software specific memory attributes.
*/
#define DT_MEM_SW_MASK DT_MEM_SW_ATTR_MASK
#define DT_MEM_SW_GET(x) ((x) & DT_MEM_SW_ATTR_MASK)
#define DT_MEM_SW(x) ((x) << DT_MEM_SW_ATTR_SHIFT)
#define ATTR_SW_ALLOC_CACHE BIT(0)
#define ATTR_SW_ALLOC_NON_CACHE BIT(1)
#define ATTR_SW_ALLOC_DMA BIT(2)
#define DT_MEM_SW_ALLOC_CACHE DT_MEM_SW(ATTR_SW_ALLOC_CACHE)
#define DT_MEM_SW_ALLOC_NON_CACHE DT_MEM_SW(ATTR_SW_ALLOC_NON_CACHE)
#define DT_MEM_SW_ALLOC_DMA DT_MEM_SW(ATTR_SW_ALLOC_DMA)
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_MEM_ATTR_SW_H_ */

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2023 Carlo Caione, <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_MEM_ATTR_HEAP_H_
#define ZEPHYR_INCLUDE_MEM_ATTR_HEAP_H_
/**
* @brief Memory heaps based on memory attributes
* @defgroup memory_attr_heap Memory heaps based on memory attributes
* @ingroup mem_mgmt
* @{
*/
#include <zephyr/mem_mgmt/mem_attr.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Init the memory pool
*
* This must be the first function to be called to initialize the memory pools
* from all the memory regions with the a software attribute.
*
* @retval 0 on success.
* @retval -EALREADY if the pool was already initialized.
* @retval -ENOMEM too many regions already allocated.
*/
int mem_attr_heap_pool_init(void);
/**
* @brief Allocate memory with a specified attribute and size.
*
* Allocates a block of memory of the specified size in bytes and with a
* specified capability / attribute. The attribute is used to select the
* correct memory heap to allocate memory from.
*
* @param attr capability / attribute requested for the memory block.
* @param bytes requested size of the allocation in bytes.
*
* @retval ptr a valid pointer to the allocated memory.
* @retval NULL if no memory is available with that attribute and size.
*/
void *mem_attr_heap_alloc(uint32_t attr, size_t bytes);
/**
* @brief Allocate aligned memory with a specified attribute, size and alignment.
*
* Allocates a block of memory of the specified size in bytes and with a
* specified capability / attribute. Takes an additional parameter specifying a
* power of two alignment in bytes.
*
* @param attr capability / attribute requested for the memory block.
* @param align power of two alignment for the returned pointer in bytes.
* @param bytes requested size of the allocation in bytes.
*
* @retval ptr a valid pointer to the allocated memory.
* @retval NULL if no memory is available with that attribute and size.
*/
void *mem_attr_heap_aligned_alloc(uint32_t attr, size_t align, size_t bytes);
/**
* @brief Free the allocated memory
*
* Used to free the passed block of memory that must be the return value of a
* previously call to @ref mem_attr_heap_alloc or @ref
* mem_attr_heap_aligned_alloc.
*
* @param block block to free, must be a pointer to a block allocated by
* @ref mem_attr_heap_alloc or @ref mem_attr_heap_aligned_alloc.
*/
void mem_attr_heap_free(void *block);
/**
* @brief Get a specific memory region descriptor for a provided address
*
* Finds the memory region descriptor struct controlling the provided pointer.
*
* @param addr address to be found, must be a pointer to a block allocated by
* @ref mem_attr_heap_alloc or @ref mem_attr_heap_aligned_alloc.
*
* @retval str pointer to a memory region structure the address belongs to.
*/
const struct mem_attr_region_t *mem_attr_heap_get_region(void *addr);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_MEM_ATTR_HEAP_H_ */

View file

@ -1,3 +1,4 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
zephyr_sources_ifdef(CONFIG_MEM_ATTR mem_attr.c) zephyr_sources_ifdef(CONFIG_MEM_ATTR mem_attr.c)
zephyr_sources_ifdef(CONFIG_MEM_ATTR_HEAP mem_attr_heap.c)

View file

@ -10,3 +10,10 @@ config MEM_ATTR
time an array of the memory regions defined in the DT that can be time an array of the memory regions defined in the DT that can be
probed at run-time using several helper functions. Set to `N` if probed at run-time using several helper functions. Set to `N` if
unsure to save RODATA space. unsure to save RODATA space.
config MEM_ATTR_HEAP
bool "Memory Attributes heap allocator"
depends on MEM_ATTR
help
Enable an heap allocator based on memory attributes to dynamically
allocate memory from DeviceTree defined memory regions.

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2021 Carlo Caione, <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/sys/sys_heap.h>
#include <zephyr/mem_mgmt/mem_attr.h>
#include <zephyr/sys/multi_heap.h>
#include <zephyr/dt-bindings/memory-attr/memory-attr.h>
#include <zephyr/dt-bindings/memory-attr/memory-attr-sw.h>
struct ma_heap {
struct sys_heap heap;
uint32_t attr;
};
struct {
struct ma_heap ma_heaps[MAX_MULTI_HEAPS];
struct sys_multi_heap multi_heap;
int nheaps;
} mah_data;
static void *mah_choice(struct sys_multi_heap *m_heap, void *cfg, size_t align, size_t size)
{
uint32_t attr;
void *block;
if (size == 0) {
return NULL;
}
attr = (uint32_t)(long) cfg;
/* Set in case the user requested a non-existing attr */
block = NULL;
for (size_t hdx = 0; hdx < mah_data.nheaps; hdx++) {
struct ma_heap *h;
h = &mah_data.ma_heaps[hdx];
if (h->attr != attr) {
continue;
}
block = sys_heap_aligned_alloc(&h->heap, align, size);
if (block != NULL) {
break;
}
}
return block;
}
void mem_attr_heap_free(void *block)
{
sys_multi_heap_free(&mah_data.multi_heap, block);
}
void *mem_attr_heap_alloc(uint32_t attr, size_t bytes)
{
return sys_multi_heap_alloc(&mah_data.multi_heap,
(void *)(long) attr, bytes);
}
void *mem_attr_heap_aligned_alloc(uint32_t attr, size_t align, size_t bytes)
{
return sys_multi_heap_aligned_alloc(&mah_data.multi_heap,
(void *)(long) attr, align, bytes);
}
const struct mem_attr_region_t *mem_attr_heap_get_region(void *addr)
{
const struct sys_multi_heap_rec *heap_rec;
heap_rec = sys_multi_heap_get_heap(&mah_data.multi_heap, addr);
return (const struct mem_attr_region_t *) heap_rec->user_data;
}
static int ma_heap_add(const struct mem_attr_region_t *region, uint32_t attr)
{
struct ma_heap *mh;
struct sys_heap *h;
/* No more heaps available */
if (mah_data.nheaps >= MAX_MULTI_HEAPS) {
return -ENOMEM;
}
mh = &mah_data.ma_heaps[mah_data.nheaps++];
h = &mh->heap;
mh->attr = attr;
sys_heap_init(h, (void *) region->dt_addr, region->dt_size);
sys_multi_heap_add_heap(&mah_data.multi_heap, h, (void *) region);
return 0;
}
int mem_attr_heap_pool_init(void)
{
const struct mem_attr_region_t *regions;
static atomic_t state;
size_t num_regions;
if (!atomic_cas(&state, 0, 1)) {
return -EALREADY;
}
sys_multi_heap_init(&mah_data.multi_heap, mah_choice);
num_regions = mem_attr_get_regions(&regions);
for (size_t idx = 0; idx < num_regions; idx++) {
uint32_t sw_attr;
sw_attr = DT_MEM_SW_ATTR_GET(regions[idx].dt_attr);
/* No SW attribute is present */
if (!sw_attr) {
continue;
}
if (ma_heap_add(&regions[idx], sw_attr)) {
return -ENOMEM;
}
}
return 0;
}