Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/frontends/nine/nine_memory_helper.c
4561 views
1
/*
2
* Copyright 2020 Axel Davy <[email protected]>
3
*
4
* Permission is hereby granted, free of charge, to any person obtaining a
5
* copy of this software and associated documentation files (the "Software"),
6
* to deal in the Software without restriction, including without limitation
7
* on the rights to use, copy, modify, merge, publish, distribute, sub
8
* license, and/or sell copies of the Software, and to permit persons to whom
9
* the Software is furnished to do so, subject to the following conditions:
10
*
11
* The above copyright notice and this permission notice (including the next
12
* paragraph) shall be included in all copies or substantial portions of the
13
* Software.
14
*
15
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21
* USE OR OTHER DEALINGS IN THE SOFTWARE. */
22
23
/*
24
* Memory util function to allocate RAM backing for textures.
25
* DEFAULT textures are stored on GPU
26
* MANAGED textures have a RAM backing and upload the content to a GPU texture for use
27
* SYSTEMMEM textures are stored in RAM and are meant to be uploaded to DEFAULT textures.
28
* Basically SYSTEMMEM + DEFAULT enables to do manually what MANAGED does automatically.
29
*
30
* Once the GPU texture is created, the RAM backing of MANAGED textures can be used in
31
* two occasions:
32
* . Recreating the GPU texture (for example lod change, or GPU memory eviction)
33
* . Reading the texture content (some games do that to fill higher res versions of the texture)
34
*
35
* When a lot of textures are used, the amount of addressing space (virtual memory) taken by MANAGED
36
* and SYSTEMMEM textures can be significant and cause virtual memory exhaustion for 32 bits programs.
37
*
38
* One way to reduce the virtual memory taken is to ignore lod and delete the RAM backing of
39
* MANAGED textures once it is uploaded. If the texture is read, or evicted from GPU memory, the RAM
40
* backing would be recreated (Note that mapping the GPU memory is not acceptable as RAM memory is supposed
41
* to have smaller (fixed) stride constraints).
42
*
43
* Instead the approach taken here is to keep the RAM backing alive, but free its addressing space.
44
* In other words virtual memory usage is reduced, but the RAM usage of the app is the same.
45
* To do so, we use the memfd feature of the linux kernel. It enables to allocate a file
46
* stored in RAM and visible only to the app. We can map/unmap portions of the file as we need.
47
* When a portion is mapped, it takes virtual memory space. When it is not, it doesn't.
48
* The file is stored in RAM, and thus the access speed is the same as normal RAM. Using such
49
* file to allocate data enables to use more than 4GB RAM on 32 bits.
50
*
51
* This approach adds some overhead: when accessing mapped content the first time, pages are allocated
52
* by the system. This has a lot of overhead (several times the time to memset the area).
53
* Releasing these pages (when unmapping) has overhead too, though significantly less.
54
*
55
* This overhead however is much less significant than the overhead of downloading the GPU content.
56
* In addition, we reduce significantly the overhead spent in Gallium nine for new allocations by
57
* using the fact new contents of the file are zero-allocated. By not calling memset in Gallium nine,
58
* the overhead of page allocation happens client side, thus outside the d3d mutex. This should give
59
* a performance boost for multithreaded applications. As malloc also has this overhead (at least for
60
* large enough allocations which use mmap internally), allocating ends up faster than with the standard
61
* allocation path.
62
* By far the overhead induced by page allocation/deallocation is the biggest overhead involved in this
63
* code. It is reduced significantly with huge pages, but it is too complex to configure for the user
64
* to use it (and it has some memory management downsides too). The memset trick enables to move most of
65
* the overhead outside Nine anyway.
66
*
67
* To prevent useless unmappings quickly followed by mapping again, we do not unmap right away allocations
68
* that are not locked for access anymore. Indeed it is likely the allocation will be accessed several times
69
* in a row, for example first to fill it, then to upload it.
70
* We keep everything mapped until we reach a threshold of memory allocated. Then we use hints to prioritize
71
* which regions to unmap first. Thus virtual memory usage is only reduced when the threshold is reached.
72
*
73
* Multiple memfd files are used, each of 100MB. Thus memory usage (but not virtual memory usage) increases
74
* by amounts of 100MB. When not on x86 32 bits, we do use the standard malloc.
75
*
76
* Finally, for ease of use, we do not implement packing of allocation inside page-aligned regions.
77
* One allocation is given one page-aligned region inside a memfd file.
78
* Allocations smaller than a page (4KB on x86) go through malloc.
79
* As texture sizes are usually multiples of powers of two, allocations above the page size are typically
80
* multiples of the page size, thus space is not wasted in practice.
81
*
82
*/
83
84
#include <errno.h>
85
#include <fcntl.h>
86
#include <limits.h>
87
#include <linux/memfd.h>
88
#include <pthread.h>
89
#include <stdio.h>
90
#include <sys/mman.h>
91
#include <sys/types.h>
92
#include <sys/stat.h>
93
#include <ulimit.h>
94
#include <unistd.h>
95
96
#include "util/list.h"
97
#include "util/u_memory.h"
98
#include "util/slab.h"
99
100
#include "nine_debug.h"
101
#include "nine_memory_helper.h"
102
#include "nine_state.h"
103
104
105
#define DIVUP(a,b) (((a)+(b)-1)/(b))
106
107
/* Required alignment for allocations */
108
#define NINE_ALLOCATION_ALIGNMENT 32
109
110
#define DBG_CHANNEL (DBG_BASETEXTURE|DBG_SURFACE|DBG_VOLUME|DBG_TEXTURE|DBG_CUBETEXTURE)
111
112
/* Use memfd only for 32 bits. Check for memfd_create support */
113
#if defined(PIPE_ARCH_X86) && defined(HAVE_MEMFD_CREATE)
114
#define NINE_ENABLE_MEMFD
115
#endif
116
117
#ifdef NINE_ENABLE_MEMFD
118
119
struct nine_memfd_file_region {
120
unsigned offset;
121
unsigned size;
122
void *map; /* pointer to the mapped content of the file. Can be NULL */
123
int num_locks; /* Total number of locks blocking the munmap */
124
int num_weak_unlocks; /* Number of users which weakly block the munmap */
125
bool zero_filled;
126
struct list_head list;
127
};
128
129
struct nine_memfd_file {
130
int fd;
131
int filesize; /* Size of the file */
132
struct list_head free_regions; /* This list is sorted by the offset, and consecutive regions are merged */
133
struct list_head unmapped_allocated_regions; /* This list and the following ones are not sorted */
134
struct list_head locked_mapped_allocated_regions;
135
struct list_head weak_unlocked_mapped_allocated_regions;
136
struct list_head unlocked_mapped_allocated_regions;
137
};
138
139
/* The allocation is stored inside a memfd */
140
#define NINE_MEMFD_ALLOC 1
141
/* The allocation is part of another allocation, which is stored inside a memfd */
142
#define NINE_MEMFD_SUBALLOC 2
143
/* The allocation was allocated with malloc and will have to be freed */
144
#define NINE_MALLOC_ALLOC 3
145
/* The pointer doesn't need memory management */
146
#define NINE_EXTERNAL_ALLOC 4
147
148
struct nine_memfd_allocation {
149
struct nine_memfd_file *file; /* File in which the data is allocated */
150
struct nine_memfd_file_region *region; /* Corresponding file memory region. Max 1 allocation per region */
151
};
152
153
/* 'Suballocations' are used to represent subregions of an allocation.
154
* For example a given layer of a texture. These are not allocations,
155
* but can be accessed separately. To correctly handle accessing them,
156
* we encapsulate them into this structure. */
157
struct nine_memfd_suballocation {
158
struct nine_memfd_allocation *parent; /* Parent allocation */
159
int relative_offset; /* Offset relative to the parent */
160
};
161
162
/* A standard allocation with malloc */
163
struct nine_malloc_allocation {
164
void *buf;
165
unsigned allocation_size;
166
};
167
168
/* A pointer with no need of memory management.
169
* For example a pointer passed by the application,
170
* or a 'suballocation' inside a malloc-ed allocation. */
171
struct nine_external_allocation {
172
void *buf;
173
};
174
175
/* Encapsulates all allocations */
176
struct nine_allocation {
177
unsigned allocation_type; /* Type of allocation */
178
union {
179
struct nine_memfd_allocation memfd;
180
struct nine_memfd_suballocation submemfd;
181
struct nine_malloc_allocation malloc;
182
struct nine_external_allocation external;
183
} memory;
184
struct list_head list_free; /* for pending frees */
185
/* The fields below are only used for memfd/submemfd allocations */
186
struct list_head list_release; /* for pending releases */
187
/* Handling of the CSMT thread:
188
* API calls are singled thread (global mutex protection).
189
* However we multithreading internally (CSMT worker thread).
190
* To handle this thread, we map/lock the allocation in the
191
* main thread and increase pending_counter. When the worker thread
192
* is done with the scheduled function, the pending_counter is decreased.
193
* If pending_counter is 0, locks_on_counter can be subtracted from
194
* active_locks (in the main thread). */
195
unsigned locks_on_counter;
196
unsigned *pending_counter;
197
/* Hint from the last unlock indicating the data might be locked again soon */
198
bool weak_unlock;
199
};
200
201
struct nine_allocator {
202
struct NineDevice9 *device;
203
int page_size; /* Page size */
204
int num_fd_max; /* Max number of memfd files */
205
int min_file_size; /* Minimum memfd file size */
206
/* Tracking of all allocations */
207
long long total_allocations; /* Amount of memory allocated */
208
long long total_locked_memory; /* TODO */ /* Amount of memory blocked by a lock */
209
long long total_virtual_memory; /* Current virtual memory used by our allocations */
210
long long total_virtual_memory_limit; /* Target maximum virtual memory used. Above that, tries to unmap memfd files whenever possible. */
211
212
int num_fd; /* Number of memfd files */ /* TODO release unused memfd files */
213
struct slab_mempool allocation_pool;
214
struct slab_mempool region_pool;
215
struct nine_memfd_file *memfd_pool; /* Table (of size num_fd) of memfd files */
216
struct list_head pending_releases; /* List of allocations with unlocks depending on pending_counter */ /* TODO: Elements seem removed only on flush. Destruction ? */
217
218
pthread_mutex_t mutex_pending_frees;
219
struct list_head pending_frees;
220
};
221
222
#ifdef DEBUG
223
224
static void
225
debug_dump_memfd_state(struct nine_memfd_file *memfd_file, bool details)
226
{
227
struct nine_memfd_file_region *region;
228
229
DBG("fd: %d, filesize: %d\n", memfd_file->fd, memfd_file->filesize);
230
if (!details)
231
return;
232
LIST_FOR_EACH_ENTRY(region, &memfd_file->free_regions, list) {
233
DBG("FREE block: offset %d, size %d, map=%p, locks=%d, weak=%d, z=%d\n",
234
region->offset, region->size, region->map,
235
region->num_locks, region->num_weak_unlocks, (int)region->zero_filled);
236
}
237
LIST_FOR_EACH_ENTRY(region, &memfd_file->unmapped_allocated_regions, list) {
238
DBG("UNMAPPED ALLOCATED block: offset %d, size %d, map=%p, locks=%d, weak=%d, z=%d\n",
239
region->offset, region->size, region->map,
240
region->num_locks, region->num_weak_unlocks, (int)region->zero_filled);
241
}
242
LIST_FOR_EACH_ENTRY(region, &memfd_file->locked_mapped_allocated_regions, list) {
243
DBG("LOCKED MAPPED ALLOCATED block: offset %d, size %d, map=%p, locks=%d, weak=%d, z=%d\n",
244
region->offset, region->size, region->map,
245
region->num_locks, region->num_weak_unlocks, (int)region->zero_filled);
246
}
247
LIST_FOR_EACH_ENTRY(region, &memfd_file->unlocked_mapped_allocated_regions, list) {
248
DBG("UNLOCKED MAPPED ALLOCATED block: offset %d, size %d, map=%p, locks=%d, weak=%d, z=%d\n",
249
region->offset, region->size, region->map,
250
region->num_locks, region->num_weak_unlocks, (int)region->zero_filled);
251
}
252
LIST_FOR_EACH_ENTRY(region, &memfd_file->weak_unlocked_mapped_allocated_regions, list) {
253
DBG("WEAK UNLOCKED MAPPED ALLOCATED block: offset %d, size %d, map=%p, locks=%d, weak=%d, z=%d\n",
254
region->offset, region->size, region->map,
255
region->num_locks, region->num_weak_unlocks, (int)region->zero_filled);
256
}
257
}
258
259
static void
260
debug_dump_allocation_state(struct nine_allocation *allocation)
261
{
262
switch(allocation->allocation_type) {
263
case NINE_MEMFD_ALLOC:
264
DBG("Allocation is stored in this memfd file:\n");
265
debug_dump_memfd_state(allocation->memory.memfd.file, true);
266
DBG("Allocation is offset: %d, size: %d\n",
267
allocation->memory.memfd.region->offset, allocation->memory.memfd.region->size);
268
break;
269
case NINE_MEMFD_SUBALLOC:
270
DBG("Allocation is suballocation at relative offset %d of this allocation:\n",
271
allocation->memory.submemfd.relative_offset);
272
DBG("Parent allocation is stored in this memfd file:\n");
273
debug_dump_memfd_state(allocation->memory.submemfd.parent->file, false);
274
DBG("Parent allocation is offset: %d, size: %d\n",
275
allocation->memory.submemfd.parent->region->offset,
276
allocation->memory.submemfd.parent->region->size);
277
break;
278
case NINE_MALLOC_ALLOC:
279
DBG("Allocation is a standard malloc\n");
280
break;
281
case NINE_EXTERNAL_ALLOC:
282
DBG("Allocation is a suballocation of a standard malloc or an external allocation\n");
283
break;
284
default:
285
assert(false);
286
}
287
}
288
289
#else
290
291
static void
292
debug_dump_memfd_state(struct nine_memfd_file *memfd_file, bool details)
293
{
294
(void)memfd_file;
295
(void)details;
296
}
297
298
static void
299
debug_dump_allocation_state(struct nine_allocation *allocation)
300
{
301
(void)allocation;
302
}
303
304
#endif
305
306
static void
307
debug_dump_allocator_state(struct nine_allocator *allocator)
308
{
309
DBG("SURFACE ALLOCATOR STATUS:\n");
310
DBG("Total allocated: %lld\n", allocator->total_allocations);
311
DBG("Total virtual memory locked: %lld\n", allocator->total_locked_memory);
312
DBG("Virtual memory used: %lld / %lld\n", allocator->total_virtual_memory, allocator->total_virtual_memory_limit);
313
DBG("Num memfd files: %d / %d\n", allocator->num_fd, allocator->num_fd_max);
314
}
315
316
317
/* Retrieve file used for the storage of the content of this allocation.
318
* NULL if not using memfd */
319
static struct nine_memfd_file *
320
nine_get_memfd_file_backing(struct nine_allocation *allocation)
321
{
322
if (allocation->allocation_type > NINE_MEMFD_SUBALLOC)
323
return NULL;
324
if (allocation->allocation_type == NINE_MEMFD_ALLOC)
325
return allocation->memory.memfd.file;
326
return allocation->memory.submemfd.parent->file;
327
}
328
329
/* Retrieve region used for the storage of the content of this allocation.
330
* NULL if not using memfd */
331
static struct nine_memfd_file_region *
332
nine_get_memfd_region_backing(struct nine_allocation *allocation)
333
{
334
if (allocation->allocation_type > NINE_MEMFD_SUBALLOC)
335
return NULL;
336
if (allocation->allocation_type == NINE_MEMFD_ALLOC)
337
return allocation->memory.memfd.region;
338
return allocation->memory.submemfd.parent->region;
339
}
340
341
static void move_region(struct list_head *tail, struct nine_memfd_file_region *region)
342
{
343
/* Remove from previous list (if any) */
344
list_delinit(&region->list);
345
/* Insert in new list (last) */
346
list_addtail(&region->list, tail);
347
}
348
349
#if 0
350
static void move_region_ordered(struct list_head *tail, struct nine_memfd_file_region *region)
351
{
352
struct nine_memfd_file_region *cur_region;
353
struct list_head *insertion_point = tail;
354
355
/* Remove from previous list (if any) */
356
list_delinit(&region->list);
357
358
LIST_FOR_EACH_ENTRY(cur_region, tail, list) {
359
if (cur_region->offset > region->offset)
360
break;
361
insertion_point = &cur_region->list;
362
}
363
/* Insert just before cur_region */
364
list_add(&region->list, insertion_point);
365
}
366
#endif
367
368
static void move_region_ordered_merge(struct nine_allocator *allocator, struct list_head *tail, struct nine_memfd_file_region *region)
369
{
370
struct nine_memfd_file_region *p, *cur_region = NULL, *prev_region = NULL;
371
372
/* Remove from previous list (if any) */
373
list_delinit(&region->list);
374
375
LIST_FOR_EACH_ENTRY(p, tail, list) {
376
cur_region = p;
377
if (cur_region->offset > region->offset)
378
break;
379
prev_region = cur_region;
380
}
381
382
/* Insert after prev_region and before cur_region. Try to merge */
383
if (prev_region && ((prev_region->offset + prev_region->size) == region->offset)) {
384
if (cur_region && (cur_region->offset == (region->offset + region->size))) {
385
/* Merge all three regions */
386
prev_region->size += region->size + cur_region->size;
387
prev_region->zero_filled = prev_region->zero_filled && region->zero_filled && cur_region->zero_filled;
388
list_del(&cur_region->list);
389
slab_free_st(&allocator->region_pool, region);
390
slab_free_st(&allocator->region_pool, cur_region);
391
} else {
392
prev_region->size += region->size;
393
prev_region->zero_filled = prev_region->zero_filled && region->zero_filled;
394
slab_free_st(&allocator->region_pool, region);
395
}
396
} else if (cur_region && (cur_region->offset == (region->offset + region->size))) {
397
cur_region->offset = region->offset;
398
cur_region->size += region->size;
399
cur_region->zero_filled = region->zero_filled && cur_region->zero_filled;
400
slab_free_st(&allocator->region_pool, region);
401
} else {
402
list_add(&region->list, prev_region ? &prev_region->list : tail);
403
}
404
}
405
406
static struct nine_memfd_file_region *allocate_region(struct nine_allocator *allocator, unsigned offset, unsigned size) {
407
struct nine_memfd_file_region *region = slab_alloc_st(&allocator->allocation_pool);
408
if (!region)
409
return NULL;
410
region->offset = offset;
411
region->size = size;
412
region->num_locks = 0;
413
region->num_weak_unlocks = 0;
414
region->map = NULL;
415
region->zero_filled = false;
416
list_inithead(&region->list);
417
return region;
418
}
419
420
/* Go through memfd allocated files, and try to use unused memory for the requested allocation.
421
* Returns whether it suceeded */
422
static bool
423
insert_new_allocation(struct nine_allocator *allocator, struct nine_allocation *new_allocation, unsigned allocation_size)
424
{
425
int memfd_index;
426
struct nine_memfd_file *memfd_file, *best_memfd_file;
427
struct nine_memfd_file_region *region, *best_region, *new_region;
428
429
430
/* Find the smallest - but bigger than the requested size - unused memory
431
* region inside the memfd files. */
432
int min_blocksize = INT_MAX;
433
434
for (memfd_index = 0; memfd_index < allocator->num_fd; memfd_index++) {
435
memfd_file = (void*)allocator->memfd_pool + memfd_index*sizeof(struct nine_memfd_file);
436
437
LIST_FOR_EACH_ENTRY(region, &memfd_file->free_regions, list) {
438
if (region->size <= min_blocksize && region->size >= allocation_size) {
439
min_blocksize = region->size;
440
best_region = region;
441
best_memfd_file = memfd_file;
442
}
443
}
444
if (min_blocksize == allocation_size)
445
break;
446
}
447
448
/* The allocation doesn't fit in any memfd file */
449
if (min_blocksize == INT_MAX)
450
return false;
451
452
/* Target region found */
453
/* Move from free to unmapped allocated */
454
best_region->size = DIVUP(allocation_size, allocator->page_size) * allocator->page_size;
455
assert(min_blocksize >= best_region->size);
456
move_region(&best_memfd_file->unmapped_allocated_regions, best_region);
457
new_allocation->memory.memfd.region = best_region;
458
new_allocation->memory.memfd.file = best_memfd_file;
459
460
/* If the original region is bigger than needed, add new region with remaining space */
461
min_blocksize -= best_region->size;
462
if (min_blocksize > 0) {
463
new_region = allocate_region(allocator, best_region->offset + best_region->size, min_blocksize);
464
new_region->zero_filled = best_region->zero_filled;
465
move_region_ordered_merge(allocator, &best_memfd_file->free_regions, new_region);
466
}
467
allocator->total_allocations += best_region->size;
468
return true;
469
}
470
471
/* Go through allocations with unlocks waiting on pending_counter being 0.
472
* If 0 is indeed reached, update the allocation status */
473
static void
474
nine_flush_pending_releases(struct nine_allocator *allocator)
475
{
476
struct nine_allocation *allocation, *ptr;
477
LIST_FOR_EACH_ENTRY_SAFE(allocation, ptr, &allocator->pending_releases, list_release) {
478
assert(allocation->locks_on_counter > 0);
479
/* If pending_releases reached 0, remove from the list and update the status */
480
if (*allocation->pending_counter == 0) {
481
struct nine_memfd_file *memfd_file = nine_get_memfd_file_backing(allocation);
482
struct nine_memfd_file_region *region = nine_get_memfd_region_backing(allocation);
483
region->num_locks -= allocation->locks_on_counter;
484
allocation->locks_on_counter = 0;
485
list_delinit(&allocation->list_release);
486
if (region->num_locks == 0) {
487
/* Move to the correct list */
488
if (region->num_weak_unlocks)
489
move_region(&memfd_file->weak_unlocked_mapped_allocated_regions, region);
490
else
491
move_region(&memfd_file->unlocked_mapped_allocated_regions, region);
492
allocator->total_locked_memory -= region->size;
493
}
494
}
495
}
496
}
497
498
static void
499
nine_free_internal(struct nine_allocator *allocator, struct nine_allocation *allocation);
500
501
static void
502
nine_flush_pending_frees(struct nine_allocator *allocator)
503
{
504
struct nine_allocation *allocation, *ptr;
505
506
pthread_mutex_lock(&allocator->mutex_pending_frees);
507
/* The order of release matters as suballocations are supposed to be released first */
508
LIST_FOR_EACH_ENTRY_SAFE(allocation, ptr, &allocator->pending_frees, list_free) {
509
/* Set the allocation in an unlocked state, and then free it */
510
if (allocation->allocation_type == NINE_MEMFD_ALLOC ||
511
allocation->allocation_type == NINE_MEMFD_SUBALLOC) {
512
struct nine_memfd_file *memfd_file = nine_get_memfd_file_backing(allocation);
513
struct nine_memfd_file_region *region = nine_get_memfd_region_backing(allocation);
514
if (region->num_locks != 0) {
515
region->num_locks = 0;
516
allocator->total_locked_memory -= region->size;
517
/* Useless, but to keep consistency */
518
move_region(&memfd_file->unlocked_mapped_allocated_regions, region);
519
}
520
region->num_weak_unlocks = 0;
521
allocation->weak_unlock = false;
522
allocation->locks_on_counter = 0;
523
list_delinit(&allocation->list_release);
524
}
525
list_delinit(&allocation->list_free);
526
nine_free_internal(allocator, allocation);
527
}
528
pthread_mutex_unlock(&allocator->mutex_pending_frees);
529
}
530
531
/* Try to unmap the memfd_index-th file if not already unmapped.
532
* If even_if_weak is False, will not unmap if there are weak unlocks */
533
static void
534
nine_memfd_unmap_region(struct nine_allocator *allocator,
535
struct nine_memfd_file *memfd_file,
536
struct nine_memfd_file_region *region)
537
{
538
DBG("Unmapping memfd mapped region at %d: size: %d, map=%p, locks=%d, weak=%d\n",
539
region->offset, region->size, region->map,
540
region->num_locks, region->num_weak_unlocks);
541
assert(region->map != NULL);
542
543
if (munmap(region->map, region->size) != 0)
544
fprintf(stderr, "Error on unmapping, errno=%d\n", (int)errno);
545
546
region->map = NULL;
547
/* Move from one of the mapped region list to the unmapped one */
548
move_region(&memfd_file->unmapped_allocated_regions, region);
549
allocator->total_virtual_memory -= region->size;
550
}
551
552
/* Unallocate a region of a memfd file */
553
static void
554
remove_allocation(struct nine_allocator *allocator, struct nine_memfd_file *memfd_file, struct nine_memfd_file_region *region)
555
{
556
assert(region->num_locks == 0);
557
region->num_weak_unlocks = 0;
558
/* Move from mapped region to unmapped region */
559
if (region->map) {
560
if (likely(!region->zero_filled)) {
561
/* As the region is mapped, it is likely the pages are allocated.
562
* Do the memset now for when we allocate again. It is much faster now,
563
* as the pages are allocated. */
564
DBG("memset on data=%p, size %d\n", region->map, region->size);
565
memset(region->map, 0, region->size);
566
region->zero_filled = true;
567
}
568
nine_memfd_unmap_region(allocator, memfd_file, region);
569
}
570
/* Move from unmapped region to free region */
571
allocator->total_allocations -= region->size;
572
move_region_ordered_merge(allocator, &memfd_file->free_regions, region);
573
}
574
575
/* Try to unmap the regions of the memfd_index-th file if not already unmapped.
576
* If even_if_weak is False, will not unmap if there are weak unlocks */
577
static void
578
nine_memfd_try_unmap_file(struct nine_allocator *allocator,
579
int memfd_index,
580
bool weak)
581
{
582
struct nine_memfd_file *memfd_file = (void*)allocator->memfd_pool + memfd_index*sizeof(struct nine_memfd_file);
583
struct nine_memfd_file_region *region, *ptr;
584
DBG("memfd file at %d: fd: %d, filesize: %d\n",
585
memfd_index, memfd_file->fd, memfd_file->filesize);
586
debug_dump_memfd_state(memfd_file, true);
587
LIST_FOR_EACH_ENTRY_SAFE(region, ptr,
588
weak ?
589
&memfd_file->weak_unlocked_mapped_allocated_regions :
590
&memfd_file->unlocked_mapped_allocated_regions,
591
list) {
592
nine_memfd_unmap_region(allocator, memfd_file, region);
593
}
594
}
595
596
/* Unmap files until we are below the virtual memory target limit.
597
* If unmap_everything_possible is set, ignore the limit and unmap
598
* all that can be unmapped. */
599
static void
600
nine_memfd_files_unmap(struct nine_allocator *allocator,
601
bool unmap_everything_possible)
602
{
603
long long memory_limit = unmap_everything_possible ?
604
0 : allocator->total_virtual_memory_limit;
605
int i;
606
607
/* We are below the limit. Do nothing */
608
if (memory_limit >= allocator->total_virtual_memory)
609
return;
610
611
/* Update allocations with pending releases */
612
nine_flush_pending_releases(allocator);
613
614
DBG("Trying to unmap files with no weak unlock (%lld / %lld)\n",
615
allocator->total_virtual_memory, memory_limit);
616
617
/* Try to release everything with no weak releases.
618
* Those have data not needed for a long time (and
619
* possibly ever). */
620
for (i = 0; i < allocator->num_fd; i++) {
621
nine_memfd_try_unmap_file(allocator, i, false);
622
if (memory_limit >= allocator->total_virtual_memory) {
623
return;}
624
}
625
626
DBG("Trying to unmap files even with weak unlocks (%lld / %lld)\n",
627
allocator->total_virtual_memory, memory_limit);
628
629
/* This wasn't enough. Also release files with weak releases */
630
for (i = 0; i < allocator->num_fd; i++) {
631
nine_memfd_try_unmap_file(allocator, i, true);
632
/* Stop if the target is reached */
633
if (memory_limit >= allocator->total_virtual_memory) {
634
return;}
635
}
636
637
if (!unmap_everything_possible)
638
return;
639
640
/* If there are some pending uploads, execute them,
641
* and retry. */
642
if (list_is_empty(&allocator->pending_releases)) {
643
return;}
644
nine_csmt_process(allocator->device);
645
nine_flush_pending_releases(allocator);
646
647
DBG("Retrying after flushing (%lld / %lld)\n",
648
allocator->total_virtual_memory, memory_limit);
649
650
for (i = 0; i < allocator->num_fd; i++) {
651
nine_memfd_try_unmap_file(allocator, i, false);
652
nine_memfd_try_unmap_file(allocator, i, true);
653
}
654
/* We have done all we could */
655
}
656
657
/* Map a given memfd file */
658
static bool
659
nine_memfd_region_map(struct nine_allocator *allocator, struct nine_memfd_file *memfd_file, struct nine_memfd_file_region *region)
660
{
661
if (region->map != NULL)
662
return true;
663
664
debug_dump_memfd_state(memfd_file, true);
665
nine_memfd_files_unmap(allocator, false);
666
667
void *buf = mmap(NULL, region->size, PROT_READ | PROT_WRITE, MAP_SHARED, memfd_file->fd, region->offset);
668
669
if (buf == MAP_FAILED && errno == ENOMEM) {
670
DBG("Failed to mmap a memfd file - trying to unmap other files\n");
671
nine_memfd_files_unmap(allocator, true);
672
buf = mmap(NULL, region->size, PROT_READ | PROT_WRITE, MAP_SHARED, memfd_file->fd, region->offset);
673
}
674
if (buf == MAP_FAILED) {
675
DBG("Failed to mmap a memfd file, errno=%d\n", (int)errno);
676
return false;
677
}
678
region->map = buf;
679
/* no need to move to an unlocked mapped regions list, the caller will handle the list */
680
allocator->total_virtual_memory += region->size;
681
assert((uintptr_t)buf % NINE_ALLOCATION_ALIGNMENT == 0); /* mmap should be page_size aligned, so it should be fine */
682
683
return true;
684
}
685
686
/* Allocate with memfd some memory. Returns True if successful. */
687
static bool
688
nine_memfd_allocator(struct nine_allocator *allocator,
689
struct nine_allocation *new_allocation,
690
unsigned allocation_size)
691
{
692
struct nine_memfd_file *memfd_file;
693
struct nine_memfd_file_region *region;
694
695
allocation_size = DIVUP(allocation_size, allocator->page_size) * allocator->page_size;
696
new_allocation->allocation_type = NINE_MEMFD_ALLOC;
697
new_allocation->locks_on_counter = 0;
698
new_allocation->pending_counter = NULL;
699
new_allocation->weak_unlock = false;
700
list_inithead(&new_allocation->list_free);
701
list_inithead(&new_allocation->list_release);
702
703
/* Try to find free space in a file already allocated */
704
if (insert_new_allocation(allocator, new_allocation, allocation_size))
705
return true;
706
707
/* No - allocate new memfd file */
708
709
if (allocator->num_fd == allocator->num_fd_max)
710
return false; /* Too many memfd files */
711
712
allocator->num_fd++;
713
memfd_file = (void*)allocator->memfd_pool + (allocator->num_fd-1)*sizeof(struct nine_memfd_file);
714
/* If the allocation size is above the memfd file default size, use a bigger size */
715
memfd_file->filesize = MAX2(allocation_size, allocator->min_file_size);
716
717
memfd_file->fd = memfd_create("gallium_nine_ram", 0);
718
if (memfd_file->fd == -1) {
719
DBG("Failed to created a memfd file, errno=%d\n", (int)errno);
720
allocator->num_fd--;
721
return false;
722
}
723
724
if (ftruncate(memfd_file->fd, memfd_file->filesize) != 0) {
725
DBG("Failed to resize a memfd file, errno=%d\n", (int)errno);
726
close(memfd_file->fd);
727
allocator->num_fd--;
728
return false;
729
}
730
731
list_inithead(&memfd_file->free_regions);
732
list_inithead(&memfd_file->unmapped_allocated_regions);
733
list_inithead(&memfd_file->locked_mapped_allocated_regions);
734
list_inithead(&memfd_file->unlocked_mapped_allocated_regions);
735
list_inithead(&memfd_file->weak_unlocked_mapped_allocated_regions);
736
737
/* Initialize the memfd file with empty region and the allocation */
738
region = allocate_region(allocator, 0, allocation_size);
739
region->zero_filled = true; /* ftruncate does zero-fill the new data */
740
list_add(&region->list, &memfd_file->unmapped_allocated_regions);
741
new_allocation->memory.memfd.file = memfd_file;
742
new_allocation->memory.memfd.region = region;
743
allocator->total_allocations += allocation_size;
744
745
if (allocation_size == memfd_file->filesize)
746
return true;
747
748
/* Add empty region */
749
region = allocate_region(allocator, allocation_size, memfd_file->filesize - allocation_size);
750
region->zero_filled = true; /* ftruncate does zero-fill the new data */
751
list_add(&region->list, &memfd_file->free_regions);
752
753
return true;
754
}
755
756
/* Allocate memory */
757
struct nine_allocation *
758
nine_allocate(struct nine_allocator *allocator, unsigned size)
759
{
760
761
struct nine_allocation *new_allocation = slab_alloc_st(&allocator->allocation_pool);
762
debug_dump_allocator_state(allocator);
763
if (!new_allocation)
764
return NULL;
765
766
nine_flush_pending_frees(allocator);
767
768
/* Restrict to >= page_size to prevent having too much fragmentation, as the size of
769
* allocations is rounded to the next page_size multiple. */
770
if (size >= allocator->page_size && allocator->total_virtual_memory_limit >= 0 &&
771
nine_memfd_allocator(allocator, new_allocation, size)) {
772
struct nine_memfd_file_region *region = new_allocation->memory.memfd.region;
773
if (!region->zero_filled) {
774
void *data = nine_get_pointer(allocator, new_allocation);
775
if (!data) {
776
ERR("INTERNAL MMAP FOR NEW ALLOCATION FAILED\n");
777
nine_free(allocator, new_allocation);
778
return NULL;
779
}
780
DBG("memset on data=%p, size %d\n", data, region->size);
781
memset(data, 0, region->size);
782
region->zero_filled = true;
783
/* Even though the user usually fills afterward, we don't weakrelease.
784
* The reason is suballocations don't affect the weakrelease state of their
785
* parents. Thus if only suballocations are accessed, the release would stay
786
* weak forever. */
787
nine_pointer_strongrelease(allocator, new_allocation);
788
}
789
DBG("ALLOCATION SUCCESSFUL\n");
790
debug_dump_allocation_state(new_allocation);
791
return new_allocation;
792
}
793
794
void *data = align_calloc(size, NINE_ALLOCATION_ALIGNMENT);
795
if (!data) {
796
DBG("ALLOCATION FAILED\n");
797
return NULL;
798
}
799
800
new_allocation->allocation_type = NINE_MALLOC_ALLOC;
801
new_allocation->memory.malloc.buf = data;
802
new_allocation->memory.malloc.allocation_size = size;
803
list_inithead(&new_allocation->list_free);
804
allocator->total_allocations += size;
805
allocator->total_locked_memory += size;
806
allocator->total_virtual_memory += size;
807
DBG("ALLOCATION SUCCESSFUL\n");
808
debug_dump_allocation_state(new_allocation);
809
return new_allocation;
810
}
811
812
/* Release memory */
813
static void
814
nine_free_internal(struct nine_allocator *allocator, struct nine_allocation *allocation)
815
{
816
DBG("RELEASING ALLOCATION\n");
817
debug_dump_allocation_state(allocation);
818
if (allocation->allocation_type == NINE_MALLOC_ALLOC) {
819
allocator->total_allocations -= allocation->memory.malloc.allocation_size;
820
allocator->total_locked_memory -= allocation->memory.malloc.allocation_size;
821
allocator->total_virtual_memory -= allocation->memory.malloc.allocation_size;
822
align_free(allocation->memory.malloc.buf);
823
} else if (allocation->allocation_type == NINE_MEMFD_ALLOC ||
824
allocation->allocation_type == NINE_MEMFD_SUBALLOC) {
825
struct nine_memfd_file *memfd_file = nine_get_memfd_file_backing(allocation);
826
struct nine_memfd_file_region *region = nine_get_memfd_region_backing(allocation);
827
if (allocation->weak_unlock)
828
region->num_weak_unlocks--;
829
if (allocation->allocation_type == NINE_MEMFD_ALLOC)
830
remove_allocation(allocator, memfd_file, region);
831
}
832
833
slab_free_st(&allocator->allocation_pool, allocation);
834
debug_dump_allocator_state(allocator);
835
}
836
837
838
void
839
nine_free(struct nine_allocator *allocator, struct nine_allocation *allocation)
840
{
841
nine_flush_pending_frees(allocator);
842
nine_flush_pending_releases(allocator);
843
nine_free_internal(allocator, allocation);
844
}
845
846
/* Called from the worker thread. Similar to nine_free except we are not in the main thread, thus
847
* we are disallowed to change the allocator structures except the fields reserved
848
* for the worker. In addition, the allocation is allowed to not being unlocked (the release
849
* will unlock it) */
850
void nine_free_worker(struct nine_allocator *allocator, struct nine_allocation *allocation)
851
{
852
/* Add the allocation to the list of pending allocations to free */
853
pthread_mutex_lock(&allocator->mutex_pending_frees);
854
/* The order of free matters as suballocations are supposed to be released first */
855
list_addtail(&allocation->list_free, &allocator->pending_frees);
856
pthread_mutex_unlock(&allocator->mutex_pending_frees);
857
}
858
859
/* Lock an allocation, and retrieve the pointer */
860
void *
861
nine_get_pointer(struct nine_allocator *allocator, struct nine_allocation *allocation)
862
{
863
struct nine_memfd_file *memfd_file;
864
struct nine_memfd_file_region *region;
865
866
nine_flush_pending_releases(allocator);
867
DBG("allocation_type: %d\n", allocation->allocation_type);
868
869
if (allocation->allocation_type == NINE_MALLOC_ALLOC)
870
return allocation->memory.malloc.buf;
871
if (allocation->allocation_type == NINE_EXTERNAL_ALLOC)
872
return allocation->memory.external.buf;
873
874
memfd_file = nine_get_memfd_file_backing(allocation);
875
region = nine_get_memfd_region_backing(allocation);
876
if (!nine_memfd_region_map(allocator, memfd_file, region)) {
877
DBG("Couldn't map memfd region for get_pointer\n");
878
return NULL;
879
}
880
881
move_region(&memfd_file->locked_mapped_allocated_regions, region); /* Note: redundant if region->num_locks */
882
region->num_locks++;
883
884
if (region->num_locks == 1)
885
allocator->total_locked_memory += region->size;
886
if (allocation->weak_unlock)
887
region->num_weak_unlocks--;
888
allocation->weak_unlock = false;
889
region->zero_filled = false;
890
891
892
if (allocation->allocation_type == NINE_MEMFD_ALLOC)
893
return region->map;
894
if (allocation->allocation_type == NINE_MEMFD_SUBALLOC)
895
return region->map + allocation->memory.submemfd.relative_offset;
896
897
assert(false);
898
return NULL;
899
}
900
901
/* Unlock an allocation, but with hint that we might lock again soon */
902
void
903
nine_pointer_weakrelease(struct nine_allocator *allocator, struct nine_allocation *allocation)
904
{
905
struct nine_memfd_file_region *region;
906
if (allocation->allocation_type > NINE_MEMFD_SUBALLOC)
907
return;
908
909
region = nine_get_memfd_region_backing(allocation);
910
if (!allocation->weak_unlock)
911
region->num_weak_unlocks++;
912
allocation->weak_unlock = true;
913
region->num_locks--;
914
if (region->num_locks == 0) {
915
struct nine_memfd_file *memfd_file = nine_get_memfd_file_backing(allocation);
916
allocator->total_locked_memory -= region->size;
917
move_region(&memfd_file->weak_unlocked_mapped_allocated_regions, region);
918
}
919
}
920
921
/* Unlock an allocation */
922
void
923
nine_pointer_strongrelease(struct nine_allocator *allocator, struct nine_allocation *allocation)
924
{
925
struct nine_memfd_file_region *region;
926
if (allocation->allocation_type > NINE_MEMFD_SUBALLOC)
927
return;
928
929
region = nine_get_memfd_region_backing(allocation);
930
region->num_locks--;
931
if (region->num_locks == 0) {
932
struct nine_memfd_file *memfd_file = nine_get_memfd_file_backing(allocation);
933
allocator->total_locked_memory -= region->size;
934
if (region->num_weak_unlocks)
935
move_region(&memfd_file->weak_unlocked_mapped_allocated_regions, region);
936
else
937
move_region(&memfd_file->unlocked_mapped_allocated_regions, region);
938
}
939
}
940
941
/* Delay a release to when a given counter becomes zero */
942
void
943
nine_pointer_delayedstrongrelease(struct nine_allocator *allocator, struct nine_allocation *allocation, unsigned *counter)
944
{
945
if (allocation->allocation_type > NINE_MEMFD_SUBALLOC)
946
return;
947
948
assert(allocation->pending_counter == NULL || allocation->pending_counter == counter);
949
allocation->pending_counter = counter;
950
allocation->locks_on_counter++;
951
952
if (list_is_empty(&allocation->list_release))
953
list_add(&allocation->list_release, &allocator->pending_releases);
954
}
955
956
/* Create a suballocation of an allocation */
957
struct nine_allocation *
958
nine_suballocate(struct nine_allocator* allocator, struct nine_allocation *allocation, int offset)
959
{
960
struct nine_allocation *new_allocation = slab_alloc_st(&allocator->allocation_pool);
961
if (!new_allocation)
962
return NULL;
963
964
DBG("Suballocate allocation at offset: %d\n", offset);
965
assert(allocation->allocation_type != NINE_MEMFD_SUBALLOC);
966
list_inithead(&new_allocation->list_free);
967
968
if (allocation->allocation_type != NINE_MEMFD_ALLOC) {
969
new_allocation->allocation_type = NINE_EXTERNAL_ALLOC;
970
if (allocation->allocation_type == NINE_MALLOC_ALLOC)
971
new_allocation->memory.external.buf = allocation->memory.malloc.buf + offset;
972
else
973
new_allocation->memory.external.buf = allocation->memory.external.buf + offset;
974
return new_allocation;
975
}
976
new_allocation->allocation_type = NINE_MEMFD_SUBALLOC;
977
new_allocation->memory.submemfd.parent = &allocation->memory.memfd;
978
new_allocation->memory.submemfd.relative_offset = offset;
979
new_allocation->locks_on_counter = 0;
980
new_allocation->pending_counter = NULL;
981
new_allocation->weak_unlock = false;
982
list_inithead(&new_allocation->list_release);
983
debug_dump_allocation_state(new_allocation);
984
return new_allocation;
985
}
986
987
/* Wrap an external pointer as an allocation */
988
struct nine_allocation *
989
nine_wrap_external_pointer(struct nine_allocator* allocator, void* data)
990
{
991
struct nine_allocation *new_allocation = slab_alloc_st(&allocator->allocation_pool);
992
if (!new_allocation)
993
return NULL;
994
DBG("Wrapping external pointer: %p\n", data);
995
new_allocation->allocation_type = NINE_EXTERNAL_ALLOC;
996
new_allocation->memory.external.buf = data;
997
list_inithead(&new_allocation->list_free);
998
return new_allocation;
999
}
1000
1001
struct nine_allocator *
1002
nine_allocator_create(struct NineDevice9 *device, int memfd_virtualsizelimit)
1003
{
1004
struct nine_allocator* allocator = MALLOC(sizeof(struct nine_allocator));
1005
1006
if (!allocator)
1007
return NULL;
1008
1009
allocator->device = device;
1010
allocator->page_size = sysconf(_SC_PAGESIZE);
1011
assert(allocator->page_size == 4 << 10);
1012
allocator->num_fd_max = (memfd_virtualsizelimit >= 0) ? MIN2(128, ulimit(__UL_GETOPENMAX)) : 0;
1013
allocator->min_file_size = DIVUP(100 * (1 << 20), allocator->page_size) * allocator->page_size; /* 100MB files */
1014
allocator->total_allocations = 0;
1015
allocator->total_locked_memory = 0;
1016
allocator->total_virtual_memory = 0;
1017
allocator->total_virtual_memory_limit = memfd_virtualsizelimit * (1 << 20);
1018
allocator->num_fd = 0;
1019
1020
DBG("Allocator created (ps: %d; fm: %d)\n", allocator->page_size, allocator->num_fd_max);
1021
1022
slab_create(&allocator->allocation_pool, sizeof(struct nine_allocation), 4096);
1023
slab_create(&allocator->region_pool, sizeof(struct nine_memfd_file_region), 4096);
1024
allocator->memfd_pool = CALLOC(allocator->num_fd_max, sizeof(struct nine_memfd_file));
1025
list_inithead(&allocator->pending_releases);
1026
list_inithead(&allocator->pending_frees);
1027
pthread_mutex_init(&allocator->mutex_pending_frees, NULL);
1028
return allocator;
1029
}
1030
1031
void
1032
nine_allocator_destroy(struct nine_allocator* allocator)
1033
{
1034
int i;
1035
DBG("DESTROYING ALLOCATOR\n");
1036
debug_dump_allocator_state(allocator);
1037
nine_flush_pending_releases(allocator);
1038
nine_flush_pending_frees(allocator);
1039
nine_memfd_files_unmap(allocator, true);
1040
pthread_mutex_destroy(&allocator->mutex_pending_frees);
1041
1042
assert(list_is_empty(&allocator->pending_frees));
1043
assert(list_is_empty(&allocator->pending_releases));
1044
for (i = 0; i < allocator->num_fd; i++) {
1045
debug_dump_memfd_state(&allocator->memfd_pool[i], true);
1046
assert(list_is_empty(&allocator->memfd_pool[i].locked_mapped_allocated_regions));
1047
assert(list_is_empty(&allocator->memfd_pool[i].weak_unlocked_mapped_allocated_regions));
1048
assert(list_is_empty(&allocator->memfd_pool[i].unlocked_mapped_allocated_regions));
1049
assert(list_is_singular(&allocator->memfd_pool[i].free_regions));
1050
slab_free_st(&allocator->region_pool,
1051
list_first_entry(&allocator->memfd_pool[i].free_regions,
1052
struct nine_memfd_file_region, list));
1053
close(allocator->memfd_pool[i].fd);
1054
}
1055
slab_destroy(&allocator->allocation_pool);
1056
slab_destroy(&allocator->region_pool);
1057
FREE(allocator->memfd_pool);
1058
FREE(allocator);
1059
}
1060
1061
#else
1062
1063
struct nine_allocation {
1064
unsigned is_external;
1065
void *external;
1066
};
1067
1068
struct nine_allocator {
1069
struct slab_mempool external_allocation_pool;
1070
pthread_mutex_t mutex_slab;
1071
};
1072
1073
struct nine_allocation *
1074
nine_allocate(struct nine_allocator *allocator, unsigned size)
1075
{
1076
struct nine_allocation *allocation;
1077
(void)allocator;
1078
assert(sizeof(struct nine_allocation) <= NINE_ALLOCATION_ALIGNMENT);
1079
allocation = align_calloc(size + NINE_ALLOCATION_ALIGNMENT, NINE_ALLOCATION_ALIGNMENT);
1080
allocation->is_external = false;
1081
return allocation;
1082
}
1083
1084
1085
void nine_free(struct nine_allocator *allocator, struct nine_allocation *allocation)
1086
{
1087
if (allocation->is_external) {
1088
pthread_mutex_lock(&allocator->mutex_slab);
1089
slab_free_st(&allocator->external_allocation_pool, allocation);
1090
pthread_mutex_unlock(&allocator->mutex_slab);
1091
} else
1092
align_free(allocation);
1093
}
1094
1095
void nine_free_worker(struct nine_allocator *allocator, struct nine_allocation *allocation)
1096
{
1097
nine_free(allocator, allocation);
1098
}
1099
1100
void *nine_get_pointer(struct nine_allocator *allocator, struct nine_allocation *allocation)
1101
{
1102
(void)allocator;
1103
if (allocation->is_external)
1104
return allocation->external;
1105
return (uint8_t *)allocation + NINE_ALLOCATION_ALIGNMENT;
1106
}
1107
1108
void nine_pointer_weakrelease(struct nine_allocator *allocator, struct nine_allocation *allocation)
1109
{
1110
(void)allocator;
1111
(void)allocation;
1112
}
1113
1114
void nine_pointer_strongrelease(struct nine_allocator *allocator, struct nine_allocation *allocation)
1115
{
1116
(void)allocator;
1117
(void)allocation;
1118
}
1119
1120
void nine_pointer_delayedstrongrelease(struct nine_allocator *allocator,
1121
struct nine_allocation *allocation,
1122
unsigned *counter)
1123
{
1124
(void)allocator;
1125
(void)allocation;
1126
(void)counter;
1127
}
1128
1129
struct nine_allocation *
1130
nine_suballocate(struct nine_allocator* allocator, struct nine_allocation *allocation, int offset)
1131
{
1132
struct nine_allocation *new_allocation;
1133
pthread_mutex_lock(&allocator->mutex_slab);
1134
new_allocation = slab_alloc_st(&allocator->external_allocation_pool);
1135
pthread_mutex_unlock(&allocator->mutex_slab);
1136
new_allocation->is_external = true;
1137
new_allocation->external = (uint8_t *)allocation + NINE_ALLOCATION_ALIGNMENT + offset;
1138
return new_allocation;
1139
}
1140
1141
struct nine_allocation *
1142
nine_wrap_external_pointer(struct nine_allocator* allocator, void* data)
1143
{
1144
struct nine_allocation *new_allocation;
1145
pthread_mutex_lock(&allocator->mutex_slab);
1146
new_allocation = slab_alloc_st(&allocator->external_allocation_pool);
1147
pthread_mutex_unlock(&allocator->mutex_slab);
1148
new_allocation->is_external = true;
1149
new_allocation->external = data;
1150
return new_allocation;
1151
}
1152
1153
struct nine_allocator *
1154
nine_allocator_create(struct NineDevice9 *device, int memfd_virtualsizelimit)
1155
{
1156
struct nine_allocator* allocator = MALLOC(sizeof(struct nine_allocator));
1157
(void)device;
1158
(void)memfd_virtualsizelimit;
1159
1160
if (!allocator)
1161
return NULL;
1162
1163
slab_create(&allocator->external_allocation_pool, sizeof(struct nine_allocation), 4096);
1164
pthread_mutex_init(&allocator->mutex_slab, NULL);
1165
1166
return allocator;
1167
}
1168
1169
void
1170
nine_allocator_destroy(struct nine_allocator *allocator)
1171
{
1172
slab_destroy(&allocator->external_allocation_pool);
1173
pthread_mutex_destroy(&allocator->mutex_slab);
1174
}
1175
1176
#endif /* NINE_ENABLE_MEMFD */
1177
1178