MOS Source Code
Loading...
Searching...
No Matches
slab.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/mm/slab.hpp"
4
5#include "mos/assert.hpp"
7#include "mos/misc/setup.hpp"
8#include "mos/mm/mm.hpp"
9
10#include <algorithm>
11#include <mos/allocator.hpp>
14#include <mos/mos_global.h>
15#include <mos_stdlib.hpp>
16#include <mos_string.hpp>
17
19{
21};
22
24{
25 size_t pages;
26 size_t size;
27};
28
29// larger slab sizes are not required, they can be allocated directly by allocating pages
30static const struct
31{
32 size_t size;
33 const char *name;
35 { 4, "builtin-4" }, { 8, "builtin-8" }, { 16, "builtin-16" }, { 24, "builtin-24" }, //
36 { 32, "builtin-32" }, { 48, "builtin-48" }, { 64, "builtin-64" }, { 96, "builtin-96" }, //
37 { 128, "builtin-128" }, { 256, "builtin-256" }, { 384, "builtin-384" }, { 512, "builtin-512" }, //
38 { 1024, "builtin-1024" },
39};
40
43
44static inline slab_t *slab_for(size_t size)
45{
46 for (size_t i = 0; i < MOS_ARRAY_SIZE(slabs); i++)
47 {
48 slab_t *slab = &slabs[i];
49 if (slab->ent_size >= size)
50 return slab;
51 }
52 return NULL;
53}
54
55static ptr_t slab_impl_new_page(size_t n)
56{
57 phyframe_t *pages = mm_get_free_pages(n);
58 if (pages == nullptr)
59 return 0;
61 return phyframe_va(pages);
62}
63
64static void slab_impl_free_page(ptr_t page, size_t n)
65{
67 mm_free_pages(va_phyframe(page), n);
68}
69
71{
72 dInfo2<slab> << "renew slab for '" << s->name << "' with " << s->ent_size << " bytes";
74 if (unlikely(!s->first_free))
75 {
76 mos_panic("slab: failed to allocate memory for slab");
77 return;
78 }
79
80 const size_t header_offset = ALIGN_UP(sizeof(slab_header_t), s->ent_size);
81 const size_t available_size = MOS_PAGE_SIZE - header_offset;
82
83 slab_header_t *const slab_ptr = (slab_header_t *) s->first_free;
84 slab_ptr->slab = s;
85 dInfo2<slab> << "slab header is at " << (void *) slab_ptr;
86 s->first_free = (ptr_t) s->first_free + header_offset;
87
88 void **arr = (void **) s->first_free;
89 const size_t max_n = available_size / s->ent_size - 1;
90 const size_t fact = s->ent_size / sizeof(void *);
91
92 for (size_t i = 0; i < max_n; i++)
93 {
94 arr[i * fact] = &arr[(i + 1) * fact];
95 }
96 arr[max_n * fact] = NULL;
97}
98
99static void slab_init_one(slab_t *slab, const char *name, size_t size)
100{
101 MOS_ASSERT_X(size < MOS_PAGE_SIZE, "current slab implementation does not support slabs larger than a page, %zu bytes requested", size);
104 slab->lock = SPINLOCK_INIT;
105 slab->first_free = 0;
106 slab->nobjs = 0;
107 slab->name = name;
108 slab->type_name = "<unsure>";
109 slab->ent_size = size;
110}
111
112void slab_init(void)
113{
114 dInfo2<slab> << "initializing the slab allocator";
115 for (size_t i = 0; i < MOS_ARRAY_SIZE(BUILTIN_SLAB_SIZES); i++)
117}
118
120{
121 dInfo2<slab> << "slab: registering slab for '" << s->name << "' with " << s->ent_size << " bytes";
124}
125
126void *slab_alloc(size_t size)
127{
128 slab_t *const slab = slab_for(size);
129 if (likely(slab))
130 return kmemcache_alloc(slab);
131
132 const size_t page_count = ALIGN_UP_TO_PAGE(size) / MOS_PAGE_SIZE;
133 const ptr_t ret = slab_impl_new_page(page_count + 1);
134 if (!ret)
135 return NULL;
136
137 slab_metadata_t *metadata = (slab_metadata_t *) ret;
138 metadata->pages = page_count;
139 metadata->size = size;
140
141 return (void *) ((ptr_t) ret + MOS_PAGE_SIZE);
142}
143
144void *slab_calloc(size_t nmemb, size_t size)
145{
146 void *ptr = slab_alloc(nmemb * size);
147 if (!ptr)
148 return NULL;
149
150 memset(ptr, 0, nmemb * size);
151 return ptr;
152}
153
154void *slab_realloc(void *oldptr, size_t new_size)
155{
156 if (!oldptr)
157 return slab_alloc(new_size);
158
159 const ptr_t addr = (ptr_t) oldptr;
160 if (is_aligned(addr, MOS_PAGE_SIZE))
161 {
162 slab_metadata_t *metadata = (slab_metadata_t *) (addr - MOS_PAGE_SIZE);
163 if (ALIGN_UP_TO_PAGE(metadata->size) == ALIGN_UP_TO_PAGE(new_size))
164 {
165 metadata->size = new_size;
166 return oldptr;
167 }
168
169 void *new_addr = slab_alloc(new_size);
170 if (!new_addr)
171 return NULL;
172
173 memcpy(new_addr, oldptr, std::min(metadata->size, new_size));
174
175 slab_free(oldptr);
176 return new_addr;
177 }
178
179 slab_header_t *slab_header = (slab_header_t *) ALIGN_DOWN_TO_PAGE(addr);
180 slab_t *slab = slab_header->slab;
181
182 if (new_size > slab->ent_size)
183 {
184 void *new_addr = slab_alloc(new_size);
185 if (!new_addr)
186 return NULL;
187
188 memcpy(new_addr, oldptr, slab->ent_size);
189 kmemcache_free(slab, oldptr);
190 return new_addr;
191 }
192
193 return oldptr;
194}
195
196void slab_free(const void *ptr)
197{
198 dInfo2<slab> << "freeing memory at " << ptr;
199 if (!ptr)
200 return;
201
202 const ptr_t addr = (ptr_t) ptr;
203 if (is_aligned(addr, MOS_PAGE_SIZE))
204 {
205 slab_metadata_t *metadata = (slab_metadata_t *) (addr - MOS_PAGE_SIZE);
206 slab_impl_free_page((ptr_t) metadata, metadata->pages + 1);
207 return;
208 }
209
210 const slab_header_t *header = (slab_header_t *) ALIGN_DOWN_TO_PAGE(addr);
211 kmemcache_free(header->slab, ptr);
212}
213
214// ======================
215
217{
218 MOS_ASSERT_X(s->ent_size > 0, "slab: invalid slab entry size %zu", s->ent_size);
219 dInfo2<slab> << "allocating from slab '" << s->name << "'";
221
222 if (s->first_free == 0)
223 {
224 // renew a slab
226 }
227
228 ptr_t *alloc = (ptr_t *) s->first_free;
229 dCont<slab> << " -> " << (void *) alloc;
230
231 // sanitize the memory
232 MOS_ASSERT_X((ptr_t) alloc >= MOS_KERNEL_START_VADDR, "slab: invalid memory address %p", (void *) alloc);
233
234 s->first_free = *alloc; // next free entry
235 memset(alloc, 0, s->ent_size);
236
237 s->nobjs++;
239 return alloc;
240}
241
242void kmemcache_free(slab_t *s, const void *addr)
243{
244 dInfo2<slab> << "freeing from slab '" << s->name << "'";
245 if (!addr)
246 return;
247
249
250 ptr_t *new_head = (ptr_t *) addr;
251 *new_head = s->first_free;
252 s->first_free = (ptr_t) new_head;
253 s->nobjs--;
254
256}
257
258// ! sysfs support
259
261{
262 sysfs_printf(f, "%20s \t%-10s %-18s \t%-8s %s\n\n", "", "Size", "First Free", "Objects", "Type Name");
264 {
265 sysfs_printf(f, "%20s:\t%-10zu " PTR_FMT " \t%-8zu %.*s\n", //
266 slab->name.data(), //
267 slab->ent_size, //
268 slab->first_free, //
269 slab->nobjs, //
270 (int) slab->type_name.size(), //
271 slab->type_name.data() //
272 );
273 }
274
275 return true;
276}
277
278MOS_INIT(SYSFS, slab_sysfs_init)
279{
280 static sysfs_item_t slabinfo = SYSFS_RO_ITEM("slabinfo", slab_sysfs_slabinfo);
281 sysfs_register_root_file(&slabinfo);
282}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.hpp:12
#define MOS_PAGE_SIZE
Definition autoconf.h:6
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.cpp:15
MOSAPI void list_node_append(list_node_t *head, list_node_t *item)
Definition list.cpp:68
#define list_foreach(t, v, h)
Iterate over a list.
Definition list.hpp:89
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.hpp:74
list_node_t list_head
A linked list head.
Definition list.hpp:23
#define va_phyframe(va)
Definition mm.hpp:85
#define phyframe_va(frame)
Definition mm.hpp:86
#define mm_free_pages(frame, npages)
Definition mm.hpp:94
phyframe_t * mm_get_free_pages(size_t npages)
Definition mm.cpp:47
void mmstat_dec(mmstat_type_t type, size_t size)
Decrement the memory usage statistics.
Definition mmstat.cpp:38
void mmstat_inc(mmstat_type_t type, size_t size)
Increment the memory usage statistics.
Definition mmstat.cpp:32
@ MEM_SLAB
Definition mmstat.hpp:9
#define likely(x)
Definition mos_global.h:39
#define MOS_ARRAY_SIZE(x)
Definition mos_global.h:84
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:76
#define ALIGN_DOWN_TO_PAGE(addr)
Definition mos_global.h:77
#define is_aligned(ptr, alignment)
Definition mos_global.h:56
#define unlikely(x)
Definition mos_global.h:40
#define ALIGN_UP(addr, size)
Definition mos_global.h:74
#define mos_panic(fmt,...)
Definition panic.hpp:51
static void * memcpy(void *s1, const void *s2, size_t n)
Definition pb_syshdr.h:90
#define NULL
Definition pb_syshdr.h:46
static void * memset(void *s, int c, size_t n)
Definition pb_syshdr.h:101
#define MOS_KERNEL_START_VADDR
#define MOS_INIT(_comp, _fn)
Definition setup.hpp:39
mos::shared_ptr< T > ptr
void slab_free(const void *ptr)
Free a block of memory from the slab allocator.
Definition slab.cpp:196
void slab_init(void)
initialise the slab allocator
Definition slab.cpp:112
static void slab_impl_free_page(ptr_t page, size_t n)
Definition slab.cpp:64
static slab_t slabs[MOS_ARRAY_SIZE(BUILTIN_SLAB_SIZES)]
Definition slab.cpp:41
void kmemcache_free(slab_t *s, const void *addr)
Definition slab.cpp:242
static bool slab_sysfs_slabinfo(sysfs_file_t *f)
Definition slab.cpp:260
static slab_t * slab_for(size_t size)
Definition slab.cpp:44
static const struct @343043210225237007246131110303056366024110042175 BUILTIN_SLAB_SIZES[]
static void slab_allocate_mem(slab_t *s)
Definition slab.cpp:70
static void slab_init_one(slab_t *slab, const char *name, size_t size)
Definition slab.cpp:99
size_t size
Definition slab.cpp:32
void * slab_alloc(size_t size)
Allocate a block of memory from the slab allocator.
Definition slab.cpp:126
void slab_register(slab_t *s)
Definition slab.cpp:119
const char * name
Definition slab.cpp:33
static list_head slabs_list
Definition slab.cpp:42
void * slab_realloc(void *oldptr, size_t new_size)
Reallocate a block of memory from the slab allocator.
Definition slab.cpp:154
static ptr_t slab_impl_new_page(size_t n)
Definition slab.cpp:55
void * kmemcache_alloc(slab_t *s)
Definition slab.cpp:216
void * slab_calloc(size_t nmemb, size_t size)
Allocate a block of memory from the slab allocator and zero it.
Definition slab.cpp:144
#define spinlock_acquire(lock)
Definition spinlock.hpp:64
#define SPINLOCK_INIT
Definition spinlock.hpp:31
#define spinlock_release(lock)
Definition spinlock.hpp:65
slab_t * slab
Definition slab.cpp:20
size_t pages
Definition slab.cpp:25
size_t size
Definition slab.cpp:26
mos::string_view name
Definition slab.hpp:58
size_t nobjs
Definition slab.hpp:57
spinlock_t lock
Definition slab.hpp:54
ptr_t first_free
Definition slab.hpp:55
mos::string_view type_name
Definition slab.hpp:59
size_t ent_size
Definition slab.hpp:56
ssize_t sysfs_printf(sysfs_file_t *file, const char *fmt,...)
Definition sysfs.cpp:73
should_inline void sysfs_register_root_file(sysfs_item_t *item)
Register an entry in the sysfs root directory.
Definition sysfs.hpp:94
#define SYSFS_RO_ITEM(_name, _show_fn)
Definition sysfs.hpp:42
constexpr auto dInfo2
Definition syslog.hpp:151
#define f(_fmt)
Definition syslog.hpp:160
constexpr auto dCont
Definition syslog.hpp:157
#define PTR_FMT
Definition types.h:29
unsigned long ptr_t
Definition types.h:21