Skip to content

Commit aa6de29

Browse files
Andrew Boieandrewboie
authored andcommitted
lib: user mode compatible mempools
We would like to offer the capability to have memory pool heap data structures that are usable from user mode threads. The current k_mem_pool implementation uses IRQ locking and system-wide membership lists that make it incompatible with user mode constraints. However, much of the existing memory pool code can be abstracted to some common functions that are used by both k_mem_pool and the new sys_mem_pool implementations. The sys_mem_pool implementation has the following differences: * The alloc/free APIs work directly with pointers, no internal memory block structures are exposed to the end user. A pointer to the source pool is provided for allocation, but freeing memory just requires the pointer and nothing else. * k_mem_pool uses IRQ locks and required very fine-grained locking in order to not affect system latency. sys_mem_pools just use a semaphore to protect the pool data structures at the API level, since there aren't implications for system responsiveness with this kind of concurrency control. * sys_mem_pools do not support the notion of timeouts for requesting memory. * sys_mem_pools are specified at compile time with macros, just like kernel memory pools. Alternative forms of specification at runtime will be a later enhancement. Signed-off-by: Andrew Boie <[email protected]>
1 parent e3076a4 commit aa6de29

File tree

7 files changed

+586
-339
lines changed

7 files changed

+586
-339
lines changed

include/kernel.h

Lines changed: 11 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <misc/dlist.h>
2626
#include <misc/slist.h>
2727
#include <misc/util.h>
28+
#include <misc/mempool_base.h>
2829
#include <kernel_version.h>
2930
#include <random/rand32.h>
3031
#include <kernel_arch_thread.h>
@@ -3544,86 +3545,11 @@ static inline u32_t k_mem_slab_num_free_get(struct k_mem_slab *slab)
35443545
* @cond INTERNAL_HIDDEN
35453546
*/
35463547

3547-
struct k_mem_pool_lvl {
3548-
union {
3549-
u32_t *bits_p;
3550-
u32_t bits;
3551-
};
3552-
sys_dlist_t free_list;
3553-
};
3554-
35553548
struct k_mem_pool {
3556-
void *buf;
3557-
size_t max_sz;
3558-
u16_t n_max;
3559-
u8_t n_levels;
3560-
u8_t max_inline_level;
3561-
struct k_mem_pool_lvl *levels;
3549+
struct sys_mem_pool_base base;
35623550
_wait_q_t wait_q;
35633551
};
35643552

3565-
#define _ALIGN4(n) ((((n)+3)/4)*4)
3566-
3567-
#define _MPOOL_HAVE_LVL(max, min, l) (((max) >> (2*(l))) >= (min) ? 1 : 0)
3568-
3569-
#define __MPOOL_LVLS(maxsz, minsz) \
3570-
(_MPOOL_HAVE_LVL((maxsz), (minsz), 0) + \
3571-
_MPOOL_HAVE_LVL((maxsz), (minsz), 1) + \
3572-
_MPOOL_HAVE_LVL((maxsz), (minsz), 2) + \
3573-
_MPOOL_HAVE_LVL((maxsz), (minsz), 3) + \
3574-
_MPOOL_HAVE_LVL((maxsz), (minsz), 4) + \
3575-
_MPOOL_HAVE_LVL((maxsz), (minsz), 5) + \
3576-
_MPOOL_HAVE_LVL((maxsz), (minsz), 6) + \
3577-
_MPOOL_HAVE_LVL((maxsz), (minsz), 7) + \
3578-
_MPOOL_HAVE_LVL((maxsz), (minsz), 8) + \
3579-
_MPOOL_HAVE_LVL((maxsz), (minsz), 9) + \
3580-
_MPOOL_HAVE_LVL((maxsz), (minsz), 10) + \
3581-
_MPOOL_HAVE_LVL((maxsz), (minsz), 11) + \
3582-
_MPOOL_HAVE_LVL((maxsz), (minsz), 12) + \
3583-
_MPOOL_HAVE_LVL((maxsz), (minsz), 13) + \
3584-
_MPOOL_HAVE_LVL((maxsz), (minsz), 14) + \
3585-
_MPOOL_HAVE_LVL((maxsz), (minsz), 15))
3586-
3587-
#define _MPOOL_MINBLK sizeof(sys_dnode_t)
3588-
3589-
#define _MPOOL_LVLS(max, min) \
3590-
__MPOOL_LVLS((max), (min) >= _MPOOL_MINBLK ? (min) : _MPOOL_MINBLK)
3591-
3592-
/* Rounds the needed bits up to integer multiples of u32_t */
3593-
#define _MPOOL_LBIT_WORDS_UNCLAMPED(n_max, l) \
3594-
((((n_max) << (2*(l))) + 31) / 32)
3595-
3596-
/* One word gets stored free unioned with the pointer, otherwise the
3597-
* calculated unclamped value
3598-
*/
3599-
#define _MPOOL_LBIT_WORDS(n_max, l) \
3600-
(_MPOOL_LBIT_WORDS_UNCLAMPED(n_max, l) < 2 ? 0 \
3601-
: _MPOOL_LBIT_WORDS_UNCLAMPED(n_max, l))
3602-
3603-
/* How many bytes for the bitfields of a single level? */
3604-
#define _MPOOL_LBIT_BYTES(maxsz, minsz, l, n_max) \
3605-
(_MPOOL_LVLS((maxsz), (minsz)) >= (l) ? \
3606-
4 * _MPOOL_LBIT_WORDS((n_max), l) : 0)
3607-
3608-
/* Size of the bitmap array that follows the buffer in allocated memory */
3609-
#define _MPOOL_BITS_SIZE(maxsz, minsz, n_max) \
3610-
(_MPOOL_LBIT_BYTES(maxsz, minsz, 0, n_max) + \
3611-
_MPOOL_LBIT_BYTES(maxsz, minsz, 1, n_max) + \
3612-
_MPOOL_LBIT_BYTES(maxsz, minsz, 2, n_max) + \
3613-
_MPOOL_LBIT_BYTES(maxsz, minsz, 3, n_max) + \
3614-
_MPOOL_LBIT_BYTES(maxsz, minsz, 4, n_max) + \
3615-
_MPOOL_LBIT_BYTES(maxsz, minsz, 5, n_max) + \
3616-
_MPOOL_LBIT_BYTES(maxsz, minsz, 6, n_max) + \
3617-
_MPOOL_LBIT_BYTES(maxsz, minsz, 7, n_max) + \
3618-
_MPOOL_LBIT_BYTES(maxsz, minsz, 8, n_max) + \
3619-
_MPOOL_LBIT_BYTES(maxsz, minsz, 9, n_max) + \
3620-
_MPOOL_LBIT_BYTES(maxsz, minsz, 10, n_max) + \
3621-
_MPOOL_LBIT_BYTES(maxsz, minsz, 11, n_max) + \
3622-
_MPOOL_LBIT_BYTES(maxsz, minsz, 12, n_max) + \
3623-
_MPOOL_LBIT_BYTES(maxsz, minsz, 13, n_max) + \
3624-
_MPOOL_LBIT_BYTES(maxsz, minsz, 14, n_max) + \
3625-
_MPOOL_LBIT_BYTES(maxsz, minsz, 15, n_max))
3626-
36273553
/**
36283554
* INTERNAL_HIDDEN @endcond
36293555
*/
@@ -3655,13 +3581,16 @@ struct k_mem_pool {
36553581
#define K_MEM_POOL_DEFINE(name, minsz, maxsz, nmax, align) \
36563582
char __aligned(align) _mpool_buf_##name[_ALIGN4(maxsz * nmax) \
36573583
+ _MPOOL_BITS_SIZE(maxsz, minsz, nmax)]; \
3658-
struct k_mem_pool_lvl _mpool_lvls_##name[_MPOOL_LVLS(maxsz, minsz)]; \
3584+
struct sys_mem_pool_lvl _mpool_lvls_##name[_MPOOL_LVLS(maxsz, minsz)]; \
36593585
struct k_mem_pool name __in_section(_k_mem_pool, static, name) = { \
3660-
.buf = _mpool_buf_##name, \
3661-
.max_sz = maxsz, \
3662-
.n_max = nmax, \
3663-
.n_levels = _MPOOL_LVLS(maxsz, minsz), \
3664-
.levels = _mpool_lvls_##name, \
3586+
.base = { \
3587+
.buf = _mpool_buf_##name, \
3588+
.max_sz = maxsz, \
3589+
.n_max = nmax, \
3590+
.n_levels = _MPOOL_LVLS(maxsz, minsz), \
3591+
.levels = _mpool_lvls_##name, \
3592+
.flags = SYS_MEM_POOL_KERNEL \
3593+
} \
36653594
}
36663595

36673596
/**

include/misc/mempool.h

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2018 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef SYS_MEMPOOL_H
8+
#define SYS_MEMPOOL_H
9+
10+
#include <kernel.h>
11+
#include <misc/mempool_base.h>
12+
13+
struct sys_mem_pool {
14+
struct sys_mem_pool_base base;
15+
struct k_mutex *mutex;
16+
};
17+
18+
struct sys_mem_pool_block {
19+
struct sys_mem_pool *pool;
20+
u32_t level : 4;
21+
u32_t block : 28;
22+
};
23+
24+
/**
25+
* @brief Statically define system memory pool
26+
*
27+
* The memory pool's buffer contains @a n_max blocks that are @a max_size bytes
28+
* long. The memory pool allows blocks to be repeatedly partitioned into
29+
* quarters, down to blocks of @a min_size bytes long. The buffer is aligned
30+
* to a @a align -byte boundary.
31+
*
32+
* If the pool is to be accessed outside the module where it is defined, it
33+
* can be declared via
34+
*
35+
* @code extern struct sys_mem_pool <name>; @endcode
36+
*
37+
* This pool will not be in an initialized state. You will still need to
38+
* run sys_mem_pool_init() on it before using any other APIs.
39+
*
40+
* @param name Name of the memory pool.
41+
* @param kmutex Pointer to an initialized k_mutex object, used for
42+
* synchronization, declared with K_MUTEX_DEFINE().
43+
* @param minsz Size of the smallest blocks in the pool (in bytes).
44+
* @param maxsz Size of the largest blocks in the pool (in bytes).
45+
* @param nmax Number of maximum sized blocks in the pool.
46+
* @param align Alignment of the pool's buffer (power of 2).
47+
* @param section Destination binary section for pool data
48+
*/
49+
#define SYS_MEM_POOL_DEFINE(name, kmutex, minsz, maxsz, nmax, align, section) \
50+
char __aligned(align) _GENERIC_SECTION(section) \
51+
_mpool_buf_##name[_ALIGN4(maxsz * nmax) \
52+
+ _MPOOL_BITS_SIZE(maxsz, minsz, nmax)]; \
53+
struct sys_mem_pool_lvl _GENERIC_SECTION(section) \
54+
_mpool_lvls_##name[_MPOOL_LVLS(maxsz, minsz)]; \
55+
_GENERIC_SECTION(section) struct sys_mem_pool name = { \
56+
.base = { \
57+
.buf = _mpool_buf_##name, \
58+
.max_sz = maxsz, \
59+
.n_max = nmax, \
60+
.n_levels = _MPOOL_LVLS(maxsz, minsz), \
61+
.levels = _mpool_lvls_##name, \
62+
.flags = SYS_MEM_POOL_KERNEL \
63+
}, \
64+
.mutex = kmutex, \
65+
}
66+
67+
/**
68+
* @brief Initialize a memory pool
69+
*
70+
* This is intended to complete initialization of memory pools that have been
71+
* declared with SYS_MEM_POOL_DEFINE().
72+
*
73+
* @param p Memory pool to initialize
74+
*/
75+
static inline void sys_mem_pool_init(struct sys_mem_pool *p)
76+
{
77+
_sys_mem_pool_base_init(&p->base);
78+
}
79+
80+
/**
81+
* @brief Allocate a block of memory
82+
*
83+
* Allocate a chunk of memory from a memory pool. This cannot be called from
84+
* interrupt context.
85+
*
86+
* @param p Address of the memory pool
87+
* @param size Requested size of the memory block
88+
* @return A pointer to the requested memory, or NULL if none is available
89+
*/
90+
void *sys_mem_pool_alloc(struct sys_mem_pool *p, size_t size);
91+
92+
/**
93+
* @brief Free memory allocated from a memory pool
94+
*
95+
* Free memory previously allocated by sys_mem_pool_alloc().
96+
* It is safe to pass NULL to this function, in which case it is a no-op.
97+
*
98+
* @param ptr Pointer to previously allocated memory
99+
*/
100+
void sys_mem_pool_free(void *ptr);
101+
102+
#endif

include/misc/mempool_base.h

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (c) 2018 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef SYS_MEMPOOL_BASE_H
8+
#define SYS_MEMPOOL_BASE_H
9+
10+
#include <zephyr/types.h>
11+
#include <stddef.h>
12+
13+
/*
14+
* Definitions and macros used by both the IRQ-safe k_mem_pool and user-mode
15+
* compatible sys_mem_pool implementations
16+
*/
17+
18+
struct sys_mem_pool_lvl {
19+
union {
20+
u32_t *bits_p;
21+
u32_t bits;
22+
};
23+
sys_dlist_t free_list;
24+
};
25+
26+
#define SYS_MEM_POOL_KERNEL 0
27+
#define SYS_MEM_POOL_USER 1
28+
29+
struct sys_mem_pool_base {
30+
void *buf;
31+
size_t max_sz;
32+
u16_t n_max;
33+
u8_t n_levels;
34+
u8_t max_inline_level;
35+
struct sys_mem_pool_lvl *levels;
36+
u8_t flags;
37+
};
38+
39+
#define _ALIGN4(n) ((((n)+3)/4)*4)
40+
41+
#define _MPOOL_HAVE_LVL(max, min, l) (((max) >> (2*(l))) >= (min) ? 1 : 0)
42+
43+
#define __MPOOL_LVLS(maxsz, minsz) \
44+
(_MPOOL_HAVE_LVL((maxsz), (minsz), 0) + \
45+
_MPOOL_HAVE_LVL((maxsz), (minsz), 1) + \
46+
_MPOOL_HAVE_LVL((maxsz), (minsz), 2) + \
47+
_MPOOL_HAVE_LVL((maxsz), (minsz), 3) + \
48+
_MPOOL_HAVE_LVL((maxsz), (minsz), 4) + \
49+
_MPOOL_HAVE_LVL((maxsz), (minsz), 5) + \
50+
_MPOOL_HAVE_LVL((maxsz), (minsz), 6) + \
51+
_MPOOL_HAVE_LVL((maxsz), (minsz), 7) + \
52+
_MPOOL_HAVE_LVL((maxsz), (minsz), 8) + \
53+
_MPOOL_HAVE_LVL((maxsz), (minsz), 9) + \
54+
_MPOOL_HAVE_LVL((maxsz), (minsz), 10) + \
55+
_MPOOL_HAVE_LVL((maxsz), (minsz), 11) + \
56+
_MPOOL_HAVE_LVL((maxsz), (minsz), 12) + \
57+
_MPOOL_HAVE_LVL((maxsz), (minsz), 13) + \
58+
_MPOOL_HAVE_LVL((maxsz), (minsz), 14) + \
59+
_MPOOL_HAVE_LVL((maxsz), (minsz), 15))
60+
61+
#define _MPOOL_MINBLK sizeof(sys_dnode_t)
62+
63+
#define _MPOOL_LVLS(max, min) \
64+
__MPOOL_LVLS((max), (min) >= _MPOOL_MINBLK ? (min) : _MPOOL_MINBLK)
65+
66+
/* Rounds the needed bits up to integer multiples of u32_t */
67+
#define _MPOOL_LBIT_WORDS_UNCLAMPED(n_max, l) \
68+
((((n_max) << (2*(l))) + 31) / 32)
69+
70+
/* One word gets stored free unioned with the pointer, otherwise the
71+
* calculated unclamped value
72+
*/
73+
#define _MPOOL_LBIT_WORDS(n_max, l) \
74+
(_MPOOL_LBIT_WORDS_UNCLAMPED(n_max, l) < 2 ? 0 \
75+
: _MPOOL_LBIT_WORDS_UNCLAMPED(n_max, l))
76+
77+
/* How many bytes for the bitfields of a single level? */
78+
#define _MPOOL_LBIT_BYTES(maxsz, minsz, l, n_max) \
79+
(_MPOOL_LVLS((maxsz), (minsz)) >= (l) ? \
80+
4 * _MPOOL_LBIT_WORDS((n_max), l) : 0)
81+
82+
/* Size of the bitmap array that follows the buffer in allocated memory */
83+
#define _MPOOL_BITS_SIZE(maxsz, minsz, n_max) \
84+
(_MPOOL_LBIT_BYTES(maxsz, minsz, 0, n_max) + \
85+
_MPOOL_LBIT_BYTES(maxsz, minsz, 1, n_max) + \
86+
_MPOOL_LBIT_BYTES(maxsz, minsz, 2, n_max) + \
87+
_MPOOL_LBIT_BYTES(maxsz, minsz, 3, n_max) + \
88+
_MPOOL_LBIT_BYTES(maxsz, minsz, 4, n_max) + \
89+
_MPOOL_LBIT_BYTES(maxsz, minsz, 5, n_max) + \
90+
_MPOOL_LBIT_BYTES(maxsz, minsz, 6, n_max) + \
91+
_MPOOL_LBIT_BYTES(maxsz, minsz, 7, n_max) + \
92+
_MPOOL_LBIT_BYTES(maxsz, minsz, 8, n_max) + \
93+
_MPOOL_LBIT_BYTES(maxsz, minsz, 9, n_max) + \
94+
_MPOOL_LBIT_BYTES(maxsz, minsz, 10, n_max) + \
95+
_MPOOL_LBIT_BYTES(maxsz, minsz, 11, n_max) + \
96+
_MPOOL_LBIT_BYTES(maxsz, minsz, 12, n_max) + \
97+
_MPOOL_LBIT_BYTES(maxsz, minsz, 13, n_max) + \
98+
_MPOOL_LBIT_BYTES(maxsz, minsz, 14, n_max) + \
99+
_MPOOL_LBIT_BYTES(maxsz, minsz, 15, n_max))
100+
101+
102+
void _sys_mem_pool_base_init(struct sys_mem_pool_base *p);
103+
104+
int _sys_mem_pool_block_alloc(struct sys_mem_pool_base *p, size_t size,
105+
u32_t *level_p, u32_t *block_p, void **data_p);
106+
107+
void _sys_mem_pool_block_free(struct sys_mem_pool_base *p, u32_t level,
108+
u32_t block);
109+
110+
#endif /* SYS_MEMPOOL_BASE_H */

0 commit comments

Comments
 (0)