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
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
probed at run-time using several helper functions. Set to `N` if
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;
}