← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
ckernel_alloc.c
Go to the documentation of this file.
1#define _GNU_SOURCE
2#include "ckernel_alloc.h"
3
4#include <errno.h>
5#include <fcntl.h>
6#include <pthread.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <strings.h>
11#include <sys/mman.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <unistd.h>
15
16/* 2MB huge page size on Linux. */
17#ifndef HUGE_PAGE_SIZE
18#define HUGE_PAGE_SIZE (2UL * 1024UL * 1024UL)
19#endif
20
21typedef struct ck_huge_alloc_entry {
22 void *ptr;
23 size_t len;
24 int was_mmap;
25 struct ck_huge_alloc_entry *next;
26} ck_huge_alloc_entry_t;
27
28static pthread_mutex_t g_alloc_mutex = PTHREAD_MUTEX_INITIALIZER;
29static ck_huge_alloc_entry_t *g_alloc_list = NULL;
30
31static size_t align_up_bytes(size_t n, size_t align)
32{
33 if (align == 0) return n;
34 return (n + align - 1) & ~(align - 1);
35}
36
37static int record_allocation(void *ptr, size_t len, int was_mmap)
38{
39 ck_huge_alloc_entry_t *entry = malloc(sizeof(*entry));
40 if (!entry) {
41 return 0;
42 }
43 entry->ptr = ptr;
44 entry->len = len;
45 entry->was_mmap = was_mmap;
46 pthread_mutex_lock(&g_alloc_mutex);
47 entry->next = g_alloc_list;
48 g_alloc_list = entry;
49 pthread_mutex_unlock(&g_alloc_mutex);
50 return 1;
51}
52
53static ck_huge_alloc_entry_t *detach_allocation(void *ptr)
54{
55 pthread_mutex_lock(&g_alloc_mutex);
56 ck_huge_alloc_entry_t **node = &g_alloc_list;
57 while (*node) {
58 if ((*node)->ptr == ptr) {
59 ck_huge_alloc_entry_t *entry = *node;
60 *node = entry->next;
61 pthread_mutex_unlock(&g_alloc_mutex);
62 return entry;
63 }
64 node = &(*node)->next;
65 }
66 pthread_mutex_unlock(&g_alloc_mutex);
67 return NULL;
68}
69
70void *ck_huge_alloc(size_t bytes)
71{
72 if (bytes == 0 || bytes > SIZE_MAX - (HUGE_PAGE_SIZE - 1)) {
73 errno = ENOMEM;
74 return NULL;
75 }
76 size_t len = align_up_bytes(bytes, HUGE_PAGE_SIZE);
77
78 /* First, try explicit huge pages via mmap + MAP_HUGETLB. */
79 void *p = mmap(NULL, len,
80 PROT_READ | PROT_WRITE,
81 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
82 -1, 0);
83 if (p != MAP_FAILED) {
84 if (!record_allocation(p, len, 1)) {
85 munmap(p, len);
86 return NULL;
87 }
88 return p;
89 }
90
91 /* Fallback: aligned_alloc with transparent hugepage hint. */
92 void *q = aligned_alloc(HUGE_PAGE_SIZE, len);
93 if (!q) {
94 fprintf(stderr, "ck_huge_alloc: aligned_alloc failed for %zu bytes: %s\n",
95 len, strerror(errno));
96 return NULL;
97 }
98
99 /* Best-effort hint; ignore errors. */
100 (void)madvise(q, len, MADV_HUGEPAGE);
101 if (!record_allocation(q, len, 0)) {
102 free(q);
103 return NULL;
104 }
105 return q;
106}
107
108void ck_huge_free(void *ptr, size_t bytes)
109{
110 if (!ptr || bytes == 0) {
111 return;
112 }
113
114 ck_huge_alloc_entry_t *entry = detach_allocation(ptr);
115 if (!entry) {
116 /* Fall back to malloc/free if the allocation wasn't tracked. */
117 free(ptr);
118 return;
119 }
120
121 if (entry->was_mmap) {
122 munmap(ptr, entry->len);
123 } else {
124 free(ptr);
125 }
126
127 free(entry);
128}
129
130static int env_flag_enabled(const char *name)
131{
132 const char *value = getenv(name);
133 if (!value || !*value) {
134 return 0;
135 }
136 if (strcmp(value, "0") == 0 || strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0) {
137 return 0;
138 }
139 return 1;
140}
141
143{
144 if (!alloc) {
145 return;
146 }
147 alloc->base = NULL;
148 alloc->total_size = 0;
149 alloc->weights_base = 0;
150 alloc->activations_base = 0;
151 alloc->mapped_len = 0;
152 alloc->weights_file_size = 0;
154}
155
156#ifdef __linux__
157static int ck_bump_alloc_try_mixed(ck_bump_alloc_t *alloc, const char *weights_path)
158{
159 long page_size_raw = sysconf(_SC_PAGESIZE);
160 size_t page_size = page_size_raw > 0 ? (size_t)page_size_raw : 4096U;
161 size_t mapped_len = 0;
162 size_t weights_len = 0;
163 size_t weights_file_len = 0;
164 size_t boundary_len = 0;
165 size_t runtime_map_start = 0;
166 int fd = -1;
167 struct stat st;
168 uint8_t *base = NULL;
169
170 if (!alloc || !weights_path || !*weights_path) {
171 return -1;
172 }
173 if (alloc->weights_base > alloc->activations_base || alloc->activations_base > alloc->total_size) {
174 fprintf(stderr, "ck_bump_alloc_init: invalid bump layout for mixed fallback\n");
175 return -1;
176 }
177
178 if (alloc->total_size > SIZE_MAX - (page_size - 1)) {
179 fprintf(stderr, "ck_bump_alloc_init: bump size overflows page alignment\n");
180 return -1;
181 }
182 mapped_len = align_up_bytes(alloc->total_size, page_size);
183 weights_len = alloc->activations_base;
184 weights_file_len = weights_len - weights_len % page_size;
185 boundary_len = weights_len - weights_file_len;
186 runtime_map_start = align_up_bytes(alloc->activations_base, page_size);
187
188 fd = open(weights_path, O_RDONLY | O_CLOEXEC);
189 if (fd < 0) {
190 fprintf(stderr, "ck_bump_alloc_init: failed to open %s: %s\n", weights_path, strerror(errno));
191 return -1;
192 }
193 if (fstat(fd, &st) != 0) {
194 fprintf(stderr, "ck_bump_alloc_init: fstat failed for %s: %s\n", weights_path, strerror(errno));
195 close(fd);
196 return -1;
197 }
198 if (!S_ISREG(st.st_mode)) {
199 fprintf(stderr, "ck_bump_alloc_init: %s is not a regular file\n", weights_path);
200 close(fd);
201 return -1;
202 }
203 alloc->weights_file_size = (size_t)st.st_size;
204 if (alloc->weights_file_size > SIZE_MAX - (page_size - 1) ||
205 weights_len > align_up_bytes(alloc->weights_file_size, page_size)) {
206 fprintf(stderr,
207 "ck_bump_alloc_init: weights file too small for mixed mapping (%zu < %zu bytes)\n",
208 alloc->weights_file_size, weights_len);
209 close(fd);
210 return -1;
211 }
212
213 base = mmap(NULL, mapped_len, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
214 if (base == MAP_FAILED) {
215 fprintf(stderr, "ck_bump_alloc_init: reserve mmap failed for %zu bytes: %s\n",
216 mapped_len, strerror(errno));
217 close(fd);
218 return -1;
219 }
220
221 if (weights_file_len > 0) {
222 void *mapped = mmap(base, weights_file_len,
223 PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
224 if (mapped == MAP_FAILED) {
225 fprintf(stderr, "ck_bump_alloc_init: weights mmap failed: %s\n", strerror(errno));
226 munmap(base, mapped_len);
227 close(fd);
228 return -1;
229 }
230 }
231
232 if (boundary_len > 0) {
233 uint8_t *boundary = mmap(base + weights_file_len, page_size,
234 PROT_READ | PROT_WRITE,
235 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
236 size_t copied = 0;
237 if (boundary == MAP_FAILED) {
238 fprintf(stderr, "ck_bump_alloc_init: boundary mmap failed: %s\n", strerror(errno));
239 munmap(base, mapped_len);
240 close(fd);
241 return -1;
242 }
243 size_t available = alloc->weights_file_size > weights_file_len
244 ? alloc->weights_file_size - weights_file_len : 0;
245 size_t copy_len = available < boundary_len ? available : boundary_len;
246 while (copied < copy_len) {
247 ssize_t count = pread(fd, boundary + copied, copy_len - copied,
248 (off_t)(weights_file_len + copied));
249 if (count < 0 && errno == EINTR) {
250 continue;
251 }
252 if (count <= 0) {
253 fprintf(stderr, "ck_bump_alloc_init: boundary read failed: %s\n",
254 count < 0 ? strerror(errno) : "unexpected EOF");
255 munmap(base, mapped_len);
256 close(fd);
257 return -1;
258 }
259 copied += (size_t)count;
260 }
261 }
262
263 if (runtime_map_start < mapped_len) {
264 void *runtime = mmap(base + runtime_map_start, mapped_len - runtime_map_start,
265 PROT_READ | PROT_WRITE,
266 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
267 if (runtime == MAP_FAILED) {
268 fprintf(stderr, "ck_bump_alloc_init: runtime mmap failed: %s\n", strerror(errno));
269 munmap(base, mapped_len);
270 close(fd);
271 return -1;
272 }
273 }
274
275 close(fd);
276 alloc->base = base;
277 alloc->mapped_len = mapped_len;
279 fprintf(stderr,
280 "ck_bump_alloc_init: fallback to mixed-backed bump (%zu MiB total, weights file-backed=%zu MiB)\n",
281 alloc->total_size / (1024U * 1024U), weights_len / (1024U * 1024U));
282 return 0;
283}
284#endif
285
287 const char *weights_path,
288 size_t total_size,
289 size_t weights_base,
290 size_t activations_base)
291{
292 int force_mixed = 0;
293 int disable_mixed = 0;
294
295 if (!alloc || total_size == 0 ||
296 total_size > SIZE_MAX - (HUGE_PAGE_SIZE - 1)) {
297 fprintf(stderr, "ck_bump_alloc_init: invalid arguments\n");
298 return -1;
299 }
300
301 ck_bump_alloc_reset(alloc);
302 alloc->total_size = total_size;
303 alloc->weights_base = weights_base;
304 alloc->activations_base = activations_base;
305 force_mixed = env_flag_enabled("CK_BUMP_FORCE_MIXED");
306 disable_mixed = env_flag_enabled("CK_BUMP_DISABLE_MIXED");
307
308 if (weights_base > activations_base || activations_base > total_size) {
309 fprintf(stderr,
310 "ck_bump_alloc_init: invalid bump layout weights=%zu activations=%zu total=%zu\n",
311 weights_base, activations_base, total_size);
312 ck_bump_alloc_reset(alloc);
313 return -1;
314 }
315
316 if (!force_mixed) {
317 void *anon = ck_huge_alloc(total_size);
318 if (anon) {
319 alloc->base = (uint8_t *)anon;
320 alloc->mapped_len = align_up_bytes(total_size, HUGE_PAGE_SIZE);
321 alloc->mode = CK_BUMP_MODE_ANON;
322 return 0;
323 }
324 }
325
326#ifdef __linux__
327 if (!disable_mixed) {
328 if (ck_bump_alloc_try_mixed(alloc, weights_path) == 0) {
329 return 0;
330 }
331 }
332#else
333 (void)weights_path;
334#endif
335
336 fprintf(stderr,
337 "ck_bump_alloc_init: failed to allocate bump arena (%zu bytes, %.2f MiB)\n",
338 total_size, (double)total_size / (1024.0 * 1024.0));
339 ck_bump_alloc_reset(alloc);
340 return -1;
341}
342
344{
345 if (!alloc || !alloc->base) {
346 ck_bump_alloc_reset(alloc);
347 return;
348 }
349
350 if (alloc->mode == CK_BUMP_MODE_MIXED_FILE_BACKED) {
351 if (alloc->mapped_len > 0) {
352 munmap(alloc->base, alloc->mapped_len);
353 }
354 } else if (alloc->mode == CK_BUMP_MODE_ANON) {
355 ck_huge_free(alloc->base, alloc->total_size);
356 }
357
358 ck_bump_alloc_reset(alloc);
359}
360
362{
363 if (!alloc) {
364 return 1;
365 }
366 return alloc->mode != CK_BUMP_MODE_MIXED_FILE_BACKED;
367}
void ck_bump_alloc_free(ck_bump_alloc_t *alloc)
int ck_bump_alloc_init(ck_bump_alloc_t *alloc, const char *weights_path, size_t total_size, size_t weights_base, size_t activations_base)
static size_t align_up_bytes(size_t n, size_t align)
static ck_huge_alloc_entry_t * g_alloc_list
static int record_allocation(void *ptr, size_t len, int was_mmap)
static void ck_bump_alloc_reset(ck_bump_alloc_t *alloc)
static int env_flag_enabled(const char *name)
static ck_huge_alloc_entry_t * detach_allocation(void *ptr)
void * ck_huge_alloc(size_t bytes)
static pthread_mutex_t g_alloc_mutex
#define HUGE_PAGE_SIZE
void ck_huge_free(void *ptr, size_t bytes)
int ck_bump_alloc_needs_weight_materialization(const ck_bump_alloc_t *alloc)
@ CK_BUMP_MODE_ANON
@ CK_BUMP_MODE_MIXED_FILE_BACKED
@ CK_BUMP_MODE_UNINITIALIZED
size_t activations_base
size_t weights_file_size
ck_bump_mode_t mode
const uint8_t size_t count
Definition true_bpe.h:209