unified: rename 'memory maps' to 'memory slabs'

This better aligns with the actual functionality of the object.

Change-Id: I70abf54f994e92abd7367251089ea4f735d273fe
Signed-off-by: Benjamin Walsh <benjamin.walsh@windriver.com>
This commit is contained in:
Benjamin Walsh 2016-10-24 17:04:43 -04:00
commit 7ef0f624a7
9 changed files with 242 additions and 236 deletions

View file

@ -1,145 +0,0 @@
.. _memory_maps_v2:
Memory Maps
###########
A :dfn:`memory map` is a kernel object that allows memory blocks
to be dynamically allocated from a designated memory region.
All memory blocks in a memory map have a single fixed size,
allowing them to be allocated and released efficiently
and avoiding memory fragmentation concerns.
.. contents::
:local:
:depth: 2
Concepts
********
Any number of memory maps can be defined. Each memory map is referenced
by its memory address.
A memory map has the following key properties:
* The **block size** of each block, measured in bytes.
It must be at least 4 bytes long.
* The **number of blocks** available for allocation.
It must be greater than zero.
* A **buffer** that provides the memory for the memory map's blocks.
It must be at least "block size" times "number of blocks" bytes long.
A thread that needs to use a memory block simply allocates it from a memory
map. When the thread finishes with a memory block,
it must release the block back to the memory map so the block can be reused.
If all the blocks are currently in use, a thread can optionally wait
for one to become available.
Any number of thread may wait on an empty memory map simultaneously;
when a memory block becomes available, it is given to the highest-priority
thread that has waited the longest.
Unlike a heap, more than one memory map can be defined, if needed. This
allows for a memory map with smaller blocks and others with larger-sized
blocks. Alternatively, a memory pool object may be used.
Internal Operation
==================
A memory map's buffer is an array of fixed-size blocks,
with no wasted space between the blocks.
The memory map keeps track of unallocated blocks using a linked list;
the first 4 bytes of each unused block provide the necessary linkage.
Implementation
**************
Defining a Memory Map
=====================
A memory map is defined using a variable of type :c:type:`struct k_mem_map`.
It must then be initialized by calling :cpp:func:`k_mem_map_init()`.
The following code defines and initializes a memory map that has 6 blocks
of 400 bytes each and is aligned to a 4-byte boundary..
.. code-block:: c
struct k_mem_map my_map;
char __aligned(4) my_map_buffer[6 * 400];
k_mem_map_init(&my_map, my_map_buffer, 400, 6);
Alternatively, a memory map can be defined and initialized at compile time
by calling :c:macro:`K_MEM_MAP_DEFINE()`.
The following code has the same effect as the code segment above. Observe
that the macro defines both the memory map and its buffer.
.. code-block:: c
K_MEM_MAP_DEFINE(my_map, 400, 6, 4);
Allocating a Memory Block
=========================
A memory block is allocated by calling :cpp:func:`k_mem_map_alloc()`.
The following code builds on the example above, and waits up to 100 milliseconds
for a memory block to become available, then fills it with zeroes.
A warning is printed if a suitable block is not obtained.
.. code-block:: c
char *block_ptr;
if (k_mem_map_alloc(&my_map, &block_ptr, 100) == 0)) {
memset(block_ptr, 0, 400);
...
} else {
printf("Memory allocation time-out");
}
Releasing a Memory Block
========================
A memory block is released by calling :cpp:func:`k_mem_map_free()`.
The following code builds on the example above, and allocates a memory block,
then releases it once it is no longer needed.
.. code-block:: c
char *block_ptr;
k_mem_map_alloc(&my_map, &block_ptr, K_FOREVER);
... /* use memory block pointed at by block_ptr */
k_mem_map_free(&my_map, &block_ptr);
Suggested Uses
**************
Use a memory map to allocate and free memory in fixed-size blocks.
Use memory map blocks when sending large amounts of data from one thread
to another, to avoid unnecessary copying of the data.
Configuration Options
*********************
Related configuration options:
* None.
APIs
****
The following memory map APIs are provided by :file:`kernel.h`:
* :cpp:func:`k_mem_map_init()`
* :cpp:func:`k_mem_map_alloc()`
* :cpp:func:`k_mem_map_free()`
* :cpp:func:`k_mem_map_num_used_get()`
* :cpp:func:`k_mem_map_num_free_get()`

View file

@ -9,6 +9,6 @@ allocate memory.
.. toctree::
:maxdepth: 2
maps.rst
slabs.rst
pools.rst
heap.rst

View file

@ -0,0 +1,145 @@
.. _memory_slabs_v2:
Memory slabs
############
A :dfn:`memory slab` is a kernel object that allows memory blocks
to be dynamically allocated from a designated memory region.
All memory blocks in a memory slab have a single fixed size,
allowing them to be allocated and released efficiently
and avoiding memory fragmentation concerns.
.. contents::
:local:
:depth: 2
Concepts
********
Any number of memory slabs can be defined. Each memory slab is referenced
by its memory address.
A memory slab has the following key properties:
* The **block size** of each block, measured in bytes.
It must be at least 4 bytes long.
* The **number of blocks** available for allocation.
It must be greater than zero.
* A **buffer** that provides the memory for the memory slab's blocks.
It must be at least "block size" times "number of blocks" bytes long.
A thread that needs to use a memory block simply allocates it from a memory
slab. When the thread finishes with a memory block,
it must release the block back to the memory slab so the block can be reused.
If all the blocks are currently in use, a thread can optionally wait
for one to become available.
Any number of threads may wait on an empty memory slab simultaneously;
when a memory block becomes available, it is given to the highest-priority
thread that has waited the longest.
Unlike a heap, more than one memory slab can be defined, if needed. This
allows for a memory slab with smaller blocks and others with larger-sized
blocks. Alternatively, a memory pool object may be used.
Internal Operation
==================
A memory slab's buffer is an array of fixed-size blocks,
with no wasted space between the blocks.
The memory slab keeps track of unallocated blocks using a linked list;
the first 4 bytes of each unused block provide the necessary linkage.
Implementation
**************
Defining a Memory Slab
======================
A memory slab is defined using a variable of type :c:type:`struct k_mem_slab`.
It must then be initialized by calling :cpp:func:`k_mem_slab_init()`.
The following code defines and initializes a memory slab that has 6 blocks
of 400 bytes each and is aligned to a 4-byte boundary..
.. code-block:: c
struct k_mem_slab my_slab;
char __aligned(4) my_slab_buffer[6 * 400];
k_mem_slab_init(&my_slab, my_slab_buffer, 400, 6);
Alternatively, a memory slab can be defined and initialized at compile time
by calling :c:macro:`K_MEM_SLAB_DEFINE()`.
The following code has the same effect as the code segment above. Observe
that the macro defines both the memory slab and its buffer.
.. code-block:: c
K_MEM_SLAB_DEFINE(my_slab, 400, 6, 4);
Allocating a Memory Block
=========================
A memory block is allocated by calling :cpp:func:`k_mem_slab_alloc()`.
The following code builds on the example above, and waits up to 100 milliseconds
for a memory block to become available, then fills it with zeroes.
A warning is printed if a suitable block is not obtained.
.. code-block:: c
char *block_ptr;
if (k_mem_slab_alloc(&my_slab, &block_ptr, 100) == 0)) {
memset(block_ptr, 0, 400);
...
} else {
printf("Memory allocation time-out");
}
Releasing a Memory Block
========================
A memory block is released by calling :cpp:func:`k_mem_slab_free()`.
The following code builds on the example above, and allocates a memory block,
then releases it once it is no longer needed.
.. code-block:: c
char *block_ptr;
k_mem_slab_alloc(&my_slab, &block_ptr, K_FOREVER);
... /* use memory block pointed at by block_ptr */
k_mem_slab_free(&my_slab, &block_ptr);
Suggested Uses
**************
Use a memory slab to allocate and free memory in fixed-size blocks.
Use memory slab blocks when sending large amounts of data from one thread
to another, to avoid unnecessary copying of the data.
Configuration Options
*********************
Related configuration options:
* None.
APIs
****
The following memory slab APIs are provided by :file:`kernel.h`:
* :cpp:func:`k_mem_slab_init()`
* :cpp:func:`k_mem_slab_alloc()`
* :cpp:func:`k_mem_slab_free()`
* :cpp:func:`k_mem_slab_num_used_get()`
* :cpp:func:`k_mem_slab_num_free_get()`

View file

@ -120,6 +120,9 @@ The microkernel mailbox object type now supports the sending of asynchronous
messages using a message buffer. (The version 1 kernel only supported
asynchronous messages using a message block.)
The microkernel memory map object has been renamed to "memory slab", to better
reflect its management of equal-size memory blocks.
Task Groups
***********

View file

@ -88,7 +88,7 @@ struct k_pipe;
struct k_fifo;
struct k_lifo;
struct k_stack;
struct k_mem_map;
struct k_mem_slab;
struct k_mem_pool;
struct k_timer;
@ -1395,9 +1395,9 @@ extern void k_pipe_block_put(struct k_pipe *pipe, struct k_mem_block *block,
* memory management
*/
/* memory maps */
/* memory slabs */
struct k_mem_map {
struct k_mem_slab {
_wait_q_t wait_q;
uint32_t num_blocks;
size_t block_size;
@ -1405,65 +1405,66 @@ struct k_mem_map {
char *free_list;
uint32_t num_used;
_DEBUG_TRACING_KERNEL_OBJECTS_NEXT_PTR(k_mem_map);
_DEBUG_TRACING_KERNEL_OBJECTS_NEXT_PTR(k_mem_slab);
};
#define K_MEM_MAP_INITIALIZER(obj, map_buffer, map_block_size, map_num_blocks) \
#define K_MEM_SLAB_INITIALIZER(obj, slab_buffer, slab_block_size, \
slab_num_blocks) \
{ \
.wait_q = SYS_DLIST_STATIC_INIT(&obj.wait_q), \
.num_blocks = map_num_blocks, \
.block_size = map_block_size, \
.buffer = map_buffer, \
.num_blocks = slab_num_blocks, \
.block_size = slab_block_size, \
.buffer = slab_buffer, \
.free_list = NULL, \
.num_used = 0, \
_DEBUG_TRACING_KERNEL_OBJECTS_INIT \
}
/**
* @brief Define a memory map
* @brief Define a memory slab
*
* This declares and initializes a memory map whose buffer is aligned to
* a @a map_align -byte boundary. The new memory map can be passed to the
* kernel's memory map functions.
* This declares and initializes a memory slab whose buffer is aligned to
* a @a slab_align -byte boundary. The new memory slab can be passed to the
* kernel's memory slab functions.
*
* Note that for each of the blocks in the memory map to be aligned to
* @a map_align bytes, then @a map_block_size must be a multiple of
* @a map_align.
* Note that for each of the blocks in the memory slab to be aligned to
* @a slab_align bytes, then @a slab_block_size must be a multiple of
* @a slab_align.
*
* @param name Name of the memory map
* @param map_block_size Size of each block in the buffer (in bytes)
* @param map_num_blocks Number blocks in the buffer
* @param map_align Alignment of the memory map's buffer (power of 2)
* @param name Name of the memory slab
* @param slab_block_size Size of each block in the buffer (in bytes)
* @param slab_num_blocks Number blocks in the buffer
* @param slab_align Alignment of the memory slab's buffer (power of 2)
*/
#define K_MEM_MAP_DEFINE(name, map_block_size, map_num_blocks, map_align) \
char __noinit __aligned(map_align) \
_k_mem_map_buf_##name[(map_num_blocks) * (map_block_size)]; \
struct k_mem_map name \
__in_section(_k_mem_map_ptr, private, mem_map) = \
K_MEM_MAP_INITIALIZER(name, _k_mem_map_buf_##name, \
map_block_size, map_num_blocks)
#define K_MEM_SLAB_DEFINE(name, slab_block_size, slab_num_blocks, slab_align) \
char __noinit __aligned(slab_align) \
_k_mem_slab_buf_##name[(slab_num_blocks) * (slab_block_size)]; \
struct k_mem_slab name \
__in_section(_k_mem_map_ptr, private, mem_slab) = \
K_MEM_SLAB_INITIALIZER(name, _k_mem_slab_buf_##name, \
slab_block_size, slab_num_blocks)
/**
* @brief Initialize a memory map.
* @brief Initialize a memory slab.
*
* Initializes the memory map and creates its list of free blocks.
* Initializes the memory slab and creates its list of free blocks.
*
* @param map Pointer to the memory map object
* @param slab Pointer to the memory slab object
* @param buffer Pointer to buffer used for the blocks.
* @param block_size Size of each block, in bytes.
* @param num_blocks Number of blocks.
*
* @return N/A
*/
extern void k_mem_map_init(struct k_mem_map *map, void *buffer,
extern void k_mem_slab_init(struct k_mem_slab *slab, void *buffer,
size_t block_size, uint32_t num_blocks);
/**
* @brief Allocate a memory map block.
* @brief Allocate a memory slab block.
*
* Takes a block from the list of unused blocks.
*
* @param map Pointer to memory map object.
* @param slab Pointer to memory slab object.
* @param mem Pointer to area to receive block address.
* @param timeout Maximum time (milliseconds) to wait for allocation to
* complete. Use K_NO_WAIT to return immediately, or K_FOREVER to wait
@ -1471,20 +1472,21 @@ extern void k_mem_map_init(struct k_mem_map *map, void *buffer,
*
* @return 0 if successful, -ENOMEM if failed immediately, -EAGAIN if timed out
*/
extern int k_mem_map_alloc(struct k_mem_map *map, void **mem, int32_t timeout);
extern int k_mem_slab_alloc(struct k_mem_slab *slab, void **mem,
int32_t timeout);
/**
* @brief Free a memory map block.
* @brief Free a memory slab block.
*
* Gives block to a waiting thread if there is one, otherwise returns it to
* the list of unused blocks.
*
* @param map Pointer to memory map object.
* @param slab Pointer to memory slab object.
* @param mem Pointer to area to containing block address.
*
* @return N/A
*/
extern void k_mem_map_free(struct k_mem_map *map, void **mem);
extern void k_mem_slab_free(struct k_mem_slab *slab, void **mem);
/**
* @brief Get the number of used memory blocks
@ -1493,13 +1495,13 @@ extern void k_mem_map_free(struct k_mem_map *map, void **mem);
* specified pool. It should be used for stats purposes only as that
* value may potentially be out-of-date by the time it is used.
*
* @param map Memory map to query
* @param slab Memory slab to query
*
* @return Number of used memory blocks
*/
static inline uint32_t k_mem_map_num_used_get(struct k_mem_map *map)
static inline uint32_t k_mem_slab_num_used_get(struct k_mem_slab *slab)
{
return map->num_used;
return slab->num_used;
}
/**
@ -1509,13 +1511,13 @@ static inline uint32_t k_mem_map_num_used_get(struct k_mem_map *map)
* specified pool. It should be used for stats purposes only as that value
* may potentially be out-of-date by the time it is used.
*
* @param map Memory map to query
* @param slab Memory slab to query
*
* @return Number of unused memory blocks
*/
static inline uint32_t k_mem_map_num_free_get(struct k_mem_map *map)
static inline uint32_t k_mem_slab_num_free_get(struct k_mem_slab *slab)
{
return map->num_blocks - map->num_used;
return slab->num_blocks - slab->num_used;
}
/* memory pools */

View file

@ -398,25 +398,26 @@ static inline int task_event_recv(kevent_t legacy_event, int32_t timeout)
/* memory maps */
#define kmemory_map_t struct k_mem_map *
#define kmemory_map_t struct k_mem_slab *
static inline int task_mem_map_alloc(kmemory_map_t map, void **mptr,
int32_t timeout)
{
return _error_to_rc(k_mem_map_alloc(map, mptr, _ticks_to_ms(timeout)));
return _error_to_rc(k_mem_slab_alloc(map, mptr,
_ticks_to_ms(timeout)));
}
#define task_mem_map_free k_mem_map_free
#define task_mem_map_free k_mem_slab_free
static inline int task_mem_map_used_get(kmemory_map_t map)
{
return (int)k_mem_map_num_used_get(map);
return (int)k_mem_slab_num_used_get(map);
}
#define DEFINE_MEM_MAP(name, map_num_blocks, map_block_size) \
K_MEM_MAP_DEFINE(_k_mem_map_obj_##name, map_block_size, \
map_num_blocks, 4); \
struct k_mem_map *const name = &_k_mem_map_obj_##name
K_MEM_SLAB_DEFINE(_k_mem_map_obj_##name, map_block_size, \
map_num_blocks, 4); \
struct k_mem_slab *const name = &_k_mem_map_obj_##name
/* memory pools */

View file

@ -21,7 +21,7 @@ lib-y += $(strip \
lifo.o \
fifo.o \
stack.o \
mem_map.o \
mem_slab.o \
mem_pool.o \
msg_q.o \
mailbox.o \

View file

@ -24,73 +24,73 @@
#include <ksched.h>
#include <init.h>
extern struct k_mem_map _k_mem_map_ptr_start[];
extern struct k_mem_map _k_mem_map_ptr_end[];
extern struct k_mem_slab _k_mem_map_ptr_start[];
extern struct k_mem_slab _k_mem_map_ptr_end[];
/**
* @brief Initialize kernel memory map subsystem.
* @brief Initialize kernel memory slab subsystem.
*
* Perform any initialization of memory maps that wasn't done at build time.
* Currently this just involves creating the list of free blocks for each map.
* Perform any initialization of memory slabs that wasn't done at build time.
* Currently this just involves creating the list of free blocks for each slab.
*
* @return N/A
*/
static void create_free_list(struct k_mem_map *map)
static void create_free_list(struct k_mem_slab *slab)
{
char *p;
int j;
map->free_list = NULL;
p = map->buffer;
slab->free_list = NULL;
p = slab->buffer;
for (j = 0; j < map->num_blocks; j++) {
*(char **)p = map->free_list;
map->free_list = p;
p += map->block_size;
for (j = 0; j < slab->num_blocks; j++) {
*(char **)p = slab->free_list;
slab->free_list = p;
p += slab->block_size;
}
}
/**
* @brief Complete initialization of statically defined memory maps.
* @brief Complete initialization of statically defined memory slabs.
*
* Perform any initialization that wasn't done at build time.
*
* @return N/A
*/
static int init_mem_map_module(struct device *dev)
static int init_mem_slab_module(struct device *dev)
{
ARG_UNUSED(dev);
struct k_mem_map *map;
struct k_mem_slab *slab;
for (map = _k_mem_map_ptr_start; map < _k_mem_map_ptr_end; map++) {
create_free_list(map);
for (slab = _k_mem_map_ptr_start; slab < _k_mem_map_ptr_end; slab++) {
create_free_list(slab);
}
return 0;
}
void k_mem_map_init(struct k_mem_map *map, void *buffer,
void k_mem_slab_init(struct k_mem_slab *slab, void *buffer,
size_t block_size, uint32_t num_blocks)
{
map->num_blocks = num_blocks;
map->block_size = block_size;
map->buffer = buffer;
map->num_used = 0;
create_free_list(map);
sys_dlist_init(&map->wait_q);
SYS_TRACING_OBJ_INIT(mem_map, map);
slab->num_blocks = num_blocks;
slab->block_size = block_size;
slab->buffer = buffer;
slab->num_used = 0;
create_free_list(slab);
sys_dlist_init(&slab->wait_q);
SYS_TRACING_OBJ_INIT(micro_mem_map, slab);
}
int k_mem_map_alloc(struct k_mem_map *map, void **mem, int32_t timeout)
int k_mem_slab_alloc(struct k_mem_slab *slab, void **mem, int32_t timeout)
{
unsigned int key = irq_lock();
int result;
if (map->free_list != NULL) {
if (slab->free_list != NULL) {
/* take a free block */
*mem = map->free_list;
map->free_list = *(char **)(map->free_list);
map->num_used++;
*mem = slab->free_list;
slab->free_list = *(char **)(slab->free_list);
slab->num_used++;
result = 0;
} else if (timeout == K_NO_WAIT) {
/* don't wait for a free block to become available */
@ -98,7 +98,7 @@ int k_mem_map_alloc(struct k_mem_map *map, void **mem, int32_t timeout)
result = -ENOMEM;
} else {
/* wait for a free block or timeout */
_pend_current_thread(&map->wait_q, timeout);
_pend_current_thread(&slab->wait_q, timeout);
result = _Swap(key);
if (result == 0) {
*mem = _current->swap_data;
@ -111,10 +111,10 @@ int k_mem_map_alloc(struct k_mem_map *map, void **mem, int32_t timeout)
return result;
}
void k_mem_map_free(struct k_mem_map *map, void **mem)
void k_mem_slab_free(struct k_mem_slab *slab, void **mem)
{
int key = irq_lock();
struct k_thread *pending_thread = _unpend_first_thread(&map->wait_q);
struct k_thread *pending_thread = _unpend_first_thread(&slab->wait_q);
if (pending_thread) {
_set_thread_return_value_with_data(pending_thread, 0, *mem);
@ -125,12 +125,12 @@ void k_mem_map_free(struct k_mem_map *map, void **mem)
return;
}
} else {
**(char ***)mem = map->free_list;
map->free_list = *(char **)mem;
map->num_used--;
**(char ***)mem = slab->free_list;
slab->free_list = *(char **)mem;
slab->num_used--;
}
irq_unlock(key);
}
SYS_INIT(init_mem_map_module, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
SYS_INIT(init_mem_slab_module, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);

View file

@ -788,7 +788,7 @@ def kernel_main_c_maps():
name = map[0]
blocks = map[1]
block_size = map[2]
kernel_main_c_out("K_MEM_MAP_DEFINE(_k_mem_map_obj_%s, %s, %s, 4);\n" %
kernel_main_c_out("K_MEM_SLAB_DEFINE(_k_mem_map_obj_%s, %s, %s, 4);\n" %
(name, block_size, blocks))
@ -1126,8 +1126,8 @@ def generate_sysgen_h_obj_ids():
sem_type = 'struct k_sem *'
pipe_struct = 'k_pipe'
pipe_type = 'struct k_pipe *'
map_struct = 'k_mem_map'
map_type = 'struct k_mem_map *'
map_struct = 'k_mem_slab'
map_type = 'struct k_mem_slab *'
fifo_struct = 'k_msgq'
fifo_type = 'struct k_msgq *'
mbox_struct = 'k_mbox'