-
Notifications
You must be signed in to change notification settings - Fork 11
/
glue.c
249 lines (211 loc) · 8.37 KB
/
glue.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Authors: Hugo Lefeuvre <[email protected]>
*
* Copyright (c) 2020, NEC Laboratories Europe GmbH, NEC Corporation,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <uk/mimalloc_impl.h>
#include <uk/mimalloc.h>
#include <mimalloc.h>
#include <mimalloc-internal.h> // _mi_options_init()
#include <uk/alloc_impl.h>
#include <uk/print.h>
#include <uk/allocregion.h>
#include <uk/thread.h> // uk_thread_current()
#include <uk/page.h> // round_pgup()
#include <uk/sched.h> // uk_mimalloc_init_internal()
/* Notes of Unikraft's Mimalloc port:
*
* 1. Minimum heap size required: 256MiB, which is the size of an arena.
*
* 2. Maximum number of Mimalloc instances:
* Mimalloc's current code base relies strongly on static variables. Instead
* of heavily patching it (and maintaining the patches) we simply restrict
* the maximum number of Mimalloc instances to one.
*
* 3. Early boot time allocator:
* Mimalloc can only be initialized after pthread. However the early boot
* process including pthread's initialization itself requires a functioning
* memory allocator. We solve this problem by relying on ukallocregion during
* the early boot time. The transition to Mimalloc is triggered as soon as
* _tls_ready() returns true. We expect that this won't cause significant
* memory leak since memory allocated during EBT is typically not freed.
*
* 4. Transition EBT allocator -> Mimalloc:
* We transition as soon as the TLS has been allocated and the %fs register
* set. This is checked at every EBT allocation by inspecting
* uk_thread_current()->prv which typically points to the thread local
* storage. Since memory allocations might happen during Mimalloc's
* initialization itself (e.g. calls to malloc() by pthread) the early boot
* time allocator continues to satisfy requests until Mimalloc is ready
* (after mi_process_load() returned).
*/
/* Minimum heap size (size of an arena)
* TODO: can Mimalloc be reconfigured/patched to lower/remove this limitation?
*/
#define MIN_HEAP_SIZE 268435456L
/* Rely on ukallocregion to satisfy boot-time allocations */
static struct uk_alloc *__region_alloc;
/* make sure that the transition from boot-time allocation to Mimalloc is done
* only once: calls to malloc() during Mimalloc initialization should be
* satisfied using the boot-time allocator.
*/
static int __initialized;
static inline int _tls_ready(void)
{
/* Is the thread local storage ready? */
struct uk_thread *current = uk_thread_current();
return current && current->prv != NULL;
}
/* boot-time malloc interface */
static void uk_mimalloc_init_internal(struct uk_alloc *a);
/* NOTE: not static, this is used in the mimalloc code base to request memory
* "from the OS"
*/
void *uk_mimalloc_region_malloc(struct uk_alloc *a, size_t size)
{
/* detect call from main thread to leave boot time mode */
if (_tls_ready() && !__initialized) {
uk_pr_debug("%s: leaving early boot-time allocation mode\n",
uk_thread_current()->name);
__initialized = 1;
uk_mimalloc_init_internal(a);
/* satisfy request using new malloc interface */
return uk_malloc(a, size);
}
uk_pr_debug("allocating %zu from region allocator\n", size);
return uk_malloc(__region_alloc, size);
}
static int uk_mimalloc_region_posix_memalign(struct uk_alloc *a __unused,
void **memptr, size_t align,
size_t size)
{
uk_pr_debug("allocating %zu aligned at %zu from region allocator\n",
size, align);
return uk_posix_memalign(__region_alloc, memptr, align, size);
}
static void uk_mimalloc_region_free(struct uk_alloc *a __unused, void *ptr)
{
uk_pr_info("attempt to free memory during early boot time\n");
return uk_free(__region_alloc, ptr);
}
/* malloc interface */
static void *uk_mimalloc_malloc(struct uk_alloc *a __unused, size_t size)
{
return mi_malloc(size);
}
static void uk_mimalloc_free(struct uk_alloc *a __unused, void *ptr)
{
mi_free(ptr);
}
static void *uk_mimalloc_realloc(struct uk_alloc *a __unused, void *ptr,
size_t size)
{
return mi_realloc(ptr, size);
}
static void *uk_mimalloc_calloc(struct uk_alloc *a __unused, size_t nelem,
size_t elem_size)
{
return mi_calloc(nelem, elem_size);
}
static int uk_mimalloc_posix_memalign(struct uk_alloc *a __unused, void **p,
size_t alignment, size_t size)
{
return mi_posix_memalign(p, alignment, size);
}
static void *uk_mimalloc_memalign(struct uk_alloc *a __unused, size_t alignment,
size_t size)
{
return mi_memalign(alignment, size);
}
static void uk_mimalloc_init_internal(struct uk_alloc *a)
{
mi_process_load();
/* rebind interface to actual malloc interface */
(a)->malloc = uk_mimalloc_malloc;
(a)->calloc = uk_mimalloc_calloc;
(a)->realloc = uk_mimalloc_realloc;
(a)->posix_memalign = uk_mimalloc_posix_memalign;
(a)->memalign = uk_mimalloc_memalign;
(a)->free = uk_mimalloc_free;
/* delay this after interface rebinding to avoid using early boot
* time memory.
*/
_mi_options_init();
uk_pr_info("Successfully initialized Mimalloc\n");
}
struct uk_alloc *uk_mimalloc_init(void *base, size_t len)
{
struct uk_alloc *a;
size_t metalen;
/* TODO: This Mimalloc port does not support multiple memory regions
* yet. Because of the multiboot layout, the first region might be a
* single page, so we simply ignore it.
*/
if (len <= __PAGE_SIZE)
return NULL;
if (__region_alloc) {
uk_pr_err("mimalloc already initialized "
"(max number of instances: 1)\n");
return NULL;
}
metalen = round_pgup(sizeof(*a));
/* enough space for allocator available? */
if (metalen > len) {
uk_pr_err("Not enough space for allocator: %" __PRIsz
" B required but only %" __PRIuptr" B usable\n",
metalen, len);
return NULL;
}
/* enough space to allocate arena? */
if (len < MIN_HEAP_SIZE) {
/* Note: we don't exit, but calls to malloc will return NULL. */
uk_pr_err("Not enough space to allocate arena: %lu bytes "
"required but only %" __PRIsz" bytes usable\n",
268435456L, len);
}
/* store allocator metadata on the heap, just before the memory pool */
a = (struct uk_alloc *)base;
uk_pr_info("Initialize mimalloc allocator (early boot time mode) @ 0x%"
__PRIuptr ", len %"__PRIsz"\n", (uintptr_t)a, len);
/* register mimalloc *before* initializing the region allocator: in all
* cases we want Mimalloc to be the default allocator.
* FIXME: add uk_allocregion_init_noregister() that initializes a region
* allocator without registering it.
* FIXME: add support for maxalloc, availmem, and addmem.
*/
uk_alloc_init_malloc(a, uk_mimalloc_region_malloc, uk_calloc_compat,
uk_realloc_compat, uk_mimalloc_region_free,
uk_mimalloc_region_posix_memalign,
uk_memalign_compat, NULL /* maxalloc */,
NULL /* availmem */, NULL /* addmem */);
__region_alloc = uk_allocregion_init((void *)((uintptr_t) base +
metalen), len - metalen);
return a;
}