8b25e0b886ff9c556920dd3686cb104ec974d84b
1 // SPDX-License-Identifier: MIT
2 // SPDX-FileCopyrightText: 2024 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 #include <rseq/mempool.h>
11 #include <rseq/compiler.h>
22 #include "rseq-utils.h"
23 #include <rseq/rseq.h>
26 * rseq-mempool.c: rseq CPU-Local Storage (CLS) memory allocator.
28 * The rseq per-CPU memory allocator allows the application the request
29 * memory pools of CPU-Local memory each of containing objects of a
30 * given size (rounded to next power of 2), reserving a given virtual
31 * address size per CPU, for a given maximum number of CPUs.
33 * The per-CPU memory allocator is analogous to TLS (Thread-Local
34 * Storage) memory: TLS is Thread-Local Storage, whereas the per-CPU
35 * memory allocator provides CPU-Local Storage.
38 #define POOL_SET_NR_ENTRIES RSEQ_BITS_PER_LONG
41 * Smallest allocation should hold enough space for a free list pointer.
43 #if RSEQ_BITS_PER_LONG == 64
44 # define POOL_SET_MIN_ENTRY 3 /* Smallest item_len=8 */
46 # define POOL_SET_MIN_ENTRY 2 /* Smallest item_len=4 */
49 #define BIT_PER_ULONG (8 * sizeof(unsigned long))
51 #define MOVE_PAGES_BATCH_SIZE 4096
53 #define RANGE_HEADER_OFFSET sizeof(struct rseq_mempool_range)
55 #if RSEQ_BITS_PER_LONG == 64
56 # define DEFAULT_POISON_VALUE 0x5555555555555555ULL
58 # define DEFAULT_POISON_VALUE 0x55555555UL
61 struct free_list_node
;
63 struct free_list_node
{
64 struct free_list_node
*next
;
68 MEMPOOL_TYPE_GLOBAL
= 0, /* Default */
69 MEMPOOL_TYPE_PERCPU
= 1,
72 struct rseq_mempool_attr
{
74 void *(*mmap_func
)(void *priv
, size_t len
);
75 int (*munmap_func
)(void *priv
, void *ptr
, size_t len
);
79 int (*init_func
)(void *priv
, void *addr
, size_t len
, int cpu
);
84 enum mempool_type type
;
88 unsigned long max_nr_ranges
;
94 struct rseq_mempool_range
;
96 struct rseq_mempool_range
{
97 struct rseq_mempool_range
*next
; /* Linked list of ranges. */
98 struct rseq_mempool
*pool
; /* Backward reference to container pool. */
103 /* Pool range mmap/munmap */
107 /* Track alloc/free. */
108 unsigned long *alloc_bitmap
;
111 struct rseq_mempool
{
112 /* Head of ranges linked-list. */
113 struct rseq_mempool_range
*range_list
;
114 unsigned long nr_ranges
;
120 * The free list chains freed items on the CPU 0 address range.
121 * We should rethink this decision if false sharing between
122 * malloc/free from other CPUs and data accesses from CPU 0
123 * becomes an issue. This is a NULL-terminated singly-linked
126 struct free_list_node
*free_list_head
;
128 /* This lock protects allocation/free within the pool. */
129 pthread_mutex_t lock
;
131 struct rseq_mempool_attr attr
;
136 * Pool set entries are indexed by item_len rounded to the next power of
137 * 2. A pool set can contain NULL pool entries, in which case the next
138 * large enough entry will be used for allocation.
140 struct rseq_mempool_set
{
141 /* This lock protects add vs malloc/zmalloc within the pool set. */
142 pthread_mutex_t lock
;
143 struct rseq_mempool
*entries
[POOL_SET_NR_ENTRIES
];
147 const char *get_pool_name(const struct rseq_mempool
*pool
)
149 return pool
->name
? : "<anonymous>";
153 void *__rseq_pool_range_percpu_ptr(const struct rseq_mempool_range
*range
, int cpu
,
154 uintptr_t item_offset
, size_t stride
)
156 return range
->base
+ (stride
* cpu
) + item_offset
;
160 void rseq_percpu_zero_item(struct rseq_mempool
*pool
,
161 struct rseq_mempool_range
*range
, uintptr_t item_offset
)
165 for (i
= 0; i
< pool
->attr
.max_nr_cpus
; i
++) {
166 char *p
= __rseq_pool_range_percpu_ptr(range
, i
,
167 item_offset
, pool
->attr
.stride
);
168 memset(p
, 0, pool
->item_len
);
173 void rseq_percpu_init_item(struct rseq_mempool
*pool
,
174 struct rseq_mempool_range
*range
, uintptr_t item_offset
,
175 void *init_ptr
, size_t init_len
)
179 for (i
= 0; i
< pool
->attr
.max_nr_cpus
; i
++) {
180 char *p
= __rseq_pool_range_percpu_ptr(range
, i
,
181 item_offset
, pool
->attr
.stride
);
182 memcpy(p
, init_ptr
, init_len
);
187 void rseq_percpu_poison_item(struct rseq_mempool
*pool
,
188 struct rseq_mempool_range
*range
, uintptr_t item_offset
)
190 uintptr_t poison
= pool
->attr
.poison
;
193 for (i
= 0; i
< pool
->attr
.max_nr_cpus
; i
++) {
194 char *p
= __rseq_pool_range_percpu_ptr(range
, i
,
195 item_offset
, pool
->attr
.stride
);
198 for (offset
= 0; offset
< pool
->item_len
; offset
+= sizeof(uintptr_t))
199 *((uintptr_t *) (p
+ offset
)) = poison
;
203 /* Always inline for __builtin_return_address(0). */
204 static inline __attribute__((always_inline
))
205 void rseq_percpu_check_poison_item(const struct rseq_mempool
*pool
,
206 const struct rseq_mempool_range
*range
, uintptr_t item_offset
)
208 uintptr_t poison
= pool
->attr
.poison
;
211 if (!pool
->attr
.robust_set
)
213 for (i
= 0; i
< pool
->attr
.max_nr_cpus
; i
++) {
214 char *p
= __rseq_pool_range_percpu_ptr(range
, i
,
215 item_offset
, pool
->attr
.stride
);
218 for (offset
= 0; offset
< pool
->item_len
; offset
+= sizeof(uintptr_t)) {
221 /* Skip poison check for free-list pointer. */
222 if (i
== 0 && offset
== 0)
224 v
= *((uintptr_t *) (p
+ offset
));
226 fprintf(stderr
, "%s: Poison corruption detected (0x%lx) for pool: \"%s\" (%p), item offset: %zu, caller: %p.\n",
227 __func__
, (unsigned long) v
, get_pool_name(pool
), pool
, item_offset
, (void *) __builtin_return_address(0));
235 int rseq_mempool_range_init_numa(void *addr
, size_t len
, int cpu
, int numa_flags
)
237 unsigned long nr_pages
, page_len
;
238 int status
[MOVE_PAGES_BATCH_SIZE
];
239 int nodes
[MOVE_PAGES_BATCH_SIZE
];
240 void *pages
[MOVE_PAGES_BATCH_SIZE
];
247 page_len
= rseq_get_page_len();
248 nr_pages
= len
>> rseq_get_count_order_ulong(page_len
);
250 nodes
[0] = numa_node_of_cpu(cpu
);
254 for (size_t k
= 1; k
< RSEQ_ARRAY_SIZE(nodes
); ++k
) {
258 for (unsigned long page
= 0; page
< nr_pages
;) {
260 size_t max_k
= RSEQ_ARRAY_SIZE(pages
);
261 size_t left
= nr_pages
- page
;
267 for (size_t k
= 0; k
< max_k
; ++k
, ++page
) {
268 pages
[k
] = addr
+ (page
* page_len
);
272 ret
= move_pages(0, max_k
, pages
, nodes
, status
, numa_flags
);
278 fprintf(stderr
, "%lu pages were not migrated\n", ret
);
279 for (size_t k
= 0; k
< max_k
; ++k
) {
282 "Error while moving page %p to numa node %d: %u\n",
283 pages
[k
], nodes
[k
], -status
[k
]);
290 int rseq_mempool_range_init_numa(void *addr
__attribute__((unused
)),
291 size_t len
__attribute__((unused
)),
292 int cpu
__attribute__((unused
)),
293 int numa_flags
__attribute__((unused
)))
301 void *default_mmap_func(void *priv
__attribute__((unused
)), size_t len
)
305 base
= mmap(NULL
, len
, PROT_READ
| PROT_WRITE
,
306 MAP_ANONYMOUS
| MAP_PRIVATE
, -1, 0);
307 if (base
== MAP_FAILED
)
313 int default_munmap_func(void *priv
__attribute__((unused
)), void *ptr
, size_t len
)
315 return munmap(ptr
, len
);
319 int create_alloc_bitmap(struct rseq_mempool
*pool
, struct rseq_mempool_range
*range
)
323 count
= ((pool
->attr
.stride
>> pool
->item_order
) + BIT_PER_ULONG
- 1) / BIT_PER_ULONG
;
326 * Not being able to create the validation bitmap is an error
327 * that needs to be reported.
329 range
->alloc_bitmap
= calloc(count
, sizeof(unsigned long));
330 if (!range
->alloc_bitmap
)
336 bool addr_in_pool(const struct rseq_mempool
*pool
, void *addr
)
338 struct rseq_mempool_range
*range
;
340 for (range
= pool
->range_list
; range
; range
= range
->next
) {
341 if (addr
>= range
->base
&& addr
< range
->base
+ range
->next_unused
)
347 /* Always inline for __builtin_return_address(0). */
348 static inline __attribute__((always_inline
))
349 void check_free_list(const struct rseq_mempool
*pool
)
351 size_t total_item
= 0, total_never_allocated
= 0, total_freed
= 0,
352 max_list_traversal
= 0, traversal_iteration
= 0;
353 struct rseq_mempool_range
*range
;
355 if (!pool
->attr
.robust_set
)
358 for (range
= pool
->range_list
; range
; range
= range
->next
) {
359 total_item
+= pool
->attr
.stride
>> pool
->item_order
;
360 total_never_allocated
+= (pool
->attr
.stride
- range
->next_unused
) >> pool
->item_order
;
362 max_list_traversal
= total_item
- total_never_allocated
;
364 for (struct free_list_node
*node
= pool
->free_list_head
, *prev
= NULL
;
369 void *node_addr
= node
;
371 if (traversal_iteration
>= max_list_traversal
) {
372 fprintf(stderr
, "%s: Corrupted free-list; Possibly infinite loop in pool \"%s\" (%p), caller %p.\n",
373 __func__
, get_pool_name(pool
), pool
, __builtin_return_address(0));
377 /* Node is out of range. */
378 if (!addr_in_pool(pool
, node_addr
)) {
380 fprintf(stderr
, "%s: Corrupted free-list node %p -> [out-of-range %p] in pool \"%s\" (%p), caller %p.\n",
381 __func__
, prev
, node
, get_pool_name(pool
), pool
, __builtin_return_address(0));
383 fprintf(stderr
, "%s: Corrupted free-list node [out-of-range %p] in pool \"%s\" (%p), caller %p.\n",
384 __func__
, node
, get_pool_name(pool
), pool
, __builtin_return_address(0));
388 traversal_iteration
++;
392 if (total_never_allocated
+ total_freed
!= total_item
) {
393 fprintf(stderr
, "%s: Corrupted free-list in pool \"%s\" (%p); total-item: %zu total-never-used: %zu total-freed: %zu, caller %p.\n",
394 __func__
, get_pool_name(pool
), pool
, total_item
, total_never_allocated
, total_freed
, __builtin_return_address(0));
399 /* Always inline for __builtin_return_address(0). */
400 static inline __attribute__((always_inline
))
401 void check_range_poison(const struct rseq_mempool
*pool
,
402 const struct rseq_mempool_range
*range
)
406 for (item_offset
= 0; item_offset
< range
->next_unused
;
407 item_offset
+= pool
->item_len
)
408 rseq_percpu_check_poison_item(pool
, range
, item_offset
);
411 /* Always inline for __builtin_return_address(0). */
412 static inline __attribute__((always_inline
))
413 void check_pool_poison(const struct rseq_mempool
*pool
)
415 struct rseq_mempool_range
*range
;
417 if (!pool
->attr
.robust_set
)
419 for (range
= pool
->range_list
; range
; range
= range
->next
)
420 check_range_poison(pool
, range
);
423 /* Always inline for __builtin_return_address(0). */
424 static inline __attribute__((always_inline
))
425 void destroy_alloc_bitmap(struct rseq_mempool
*pool
, struct rseq_mempool_range
*range
)
427 unsigned long *bitmap
= range
->alloc_bitmap
;
428 size_t count
, total_leaks
= 0;
433 count
= ((pool
->attr
.stride
>> pool
->item_order
) + BIT_PER_ULONG
- 1) / BIT_PER_ULONG
;
435 /* Assert that all items in the pool were freed. */
436 for (size_t k
= 0; k
< count
; ++k
)
437 total_leaks
+= rseq_hweight_ulong(bitmap
[k
]);
439 fprintf(stderr
, "%s: Pool \"%s\" (%p) has %zu leaked items on destroy, caller: %p.\n",
440 __func__
, get_pool_name(pool
), pool
, total_leaks
, (void *) __builtin_return_address(0));
447 /* Always inline for __builtin_return_address(0). */
448 static inline __attribute__((always_inline
))
449 int rseq_mempool_range_destroy(struct rseq_mempool
*pool
,
450 struct rseq_mempool_range
*range
)
452 destroy_alloc_bitmap(pool
, range
);
453 /* range is a header located one page before the aligned mapping. */
454 return pool
->attr
.munmap_func(pool
->attr
.mmap_priv
, range
->mmap_addr
, range
->mmap_len
);
458 * Allocate a memory mapping aligned on @alignment, with an optional
459 * @pre_header before the mapping.
462 void *aligned_mmap_anonymous(struct rseq_mempool
*pool
,
463 size_t page_size
, size_t len
, size_t alignment
,
464 void **pre_header
, size_t pre_header_len
)
466 size_t minimum_page_count
, page_count
, extra
, total_allocate
= 0;
470 if (len
< page_size
|| alignment
< page_size
||
471 !is_pow2(alignment
) || (len
& (alignment
- 1))) {
475 page_order
= rseq_get_count_order_ulong(page_size
);
476 if (page_order
< 0) {
480 if (pre_header_len
&& (pre_header_len
& (page_size
- 1))) {
485 minimum_page_count
= (pre_header_len
+ len
) >> page_order
;
486 page_count
= (pre_header_len
+ len
+ alignment
- page_size
) >> page_order
;
488 assert(page_count
>= minimum_page_count
);
490 ptr
= pool
->attr
.mmap_func(pool
->attr
.mmap_priv
, page_count
<< page_order
);
494 total_allocate
= page_count
<< page_order
;
496 if (!(((uintptr_t) ptr
+ pre_header_len
) & (alignment
- 1))) {
497 /* Pointer is already aligned. ptr points to pre_header. */
501 /* Unmap extra before. */
502 extra
= offset_align((uintptr_t) ptr
+ pre_header_len
, alignment
);
503 assert(!(extra
& (page_size
- 1)));
504 if (pool
->attr
.munmap_func(pool
->attr
.mmap_priv
, ptr
, extra
)) {
508 total_allocate
-= extra
;
509 ptr
+= extra
; /* ptr points to pre_header */
510 page_count
-= extra
>> page_order
;
512 assert(page_count
>= minimum_page_count
);
514 if (page_count
> minimum_page_count
) {
517 /* Unmap extra after. */
518 extra_ptr
= ptr
+ (minimum_page_count
<< page_order
);
519 extra
= (page_count
- minimum_page_count
) << page_order
;
520 if (pool
->attr
.munmap_func(pool
->attr
.mmap_priv
, extra_ptr
, extra
)) {
524 total_allocate
-= extra
;
527 assert(!(((uintptr_t)ptr
+ pre_header_len
) & (alignment
- 1)));
528 assert(total_allocate
== len
+ pre_header_len
);
534 ptr
+= pre_header_len
;
540 struct rseq_mempool_range
*rseq_mempool_range_create(struct rseq_mempool
*pool
)
542 struct rseq_mempool_range
*range
;
543 unsigned long page_size
;
547 if (pool
->attr
.max_nr_ranges
&&
548 pool
->nr_ranges
>= pool
->attr
.max_nr_ranges
) {
552 page_size
= rseq_get_page_len();
554 base
= aligned_mmap_anonymous(pool
, page_size
,
555 pool
->attr
.stride
* pool
->attr
.max_nr_cpus
,
560 range
= (struct rseq_mempool_range
*) (base
- RANGE_HEADER_OFFSET
);
563 range
->header
= header
;
564 range
->mmap_addr
= header
;
565 range
->mmap_len
= page_size
+ (pool
->attr
.stride
* pool
->attr
.max_nr_cpus
);
566 if (pool
->attr
.robust_set
) {
567 if (create_alloc_bitmap(pool
, range
))
570 if (pool
->attr
.init_set
) {
571 switch (pool
->attr
.type
) {
572 case MEMPOOL_TYPE_GLOBAL
:
573 if (pool
->attr
.init_func(pool
->attr
.init_priv
,
574 base
, pool
->attr
.stride
, -1)) {
578 case MEMPOOL_TYPE_PERCPU
:
581 for (cpu
= 0; cpu
< pool
->attr
.max_nr_cpus
; cpu
++) {
582 if (pool
->attr
.init_func(pool
->attr
.init_priv
,
583 base
+ (pool
->attr
.stride
* cpu
),
584 pool
->attr
.stride
, cpu
)) {
598 (void) rseq_mempool_range_destroy(pool
, range
);
602 int rseq_mempool_destroy(struct rseq_mempool
*pool
)
604 struct rseq_mempool_range
*range
, *next_range
;
609 check_free_list(pool
);
610 check_pool_poison(pool
);
611 /* Iteration safe against removal. */
612 for (range
= pool
->range_list
; range
&& (next_range
= range
->next
, 1); range
= next_range
) {
613 if (rseq_mempool_range_destroy(pool
, range
))
615 /* Update list head to keep list coherent in case of partial failure. */
616 pool
->range_list
= next_range
;
618 pthread_mutex_destroy(&pool
->lock
);
625 struct rseq_mempool
*rseq_mempool_create(const char *pool_name
,
626 size_t item_len
, const struct rseq_mempool_attr
*_attr
)
628 struct rseq_mempool
*pool
;
629 struct rseq_mempool_attr attr
= {};
632 /* Make sure each item is large enough to contain free list pointers. */
633 if (item_len
< sizeof(void *))
634 item_len
= sizeof(void *);
636 /* Align item_len on next power of two. */
637 order
= rseq_get_count_order_ulong(item_len
);
642 item_len
= 1UL << order
;
645 memcpy(&attr
, _attr
, sizeof(attr
));
646 if (!attr
.mmap_set
) {
647 attr
.mmap_func
= default_mmap_func
;
648 attr
.munmap_func
= default_munmap_func
;
649 attr
.mmap_priv
= NULL
;
653 case MEMPOOL_TYPE_PERCPU
:
654 if (attr
.max_nr_cpus
< 0) {
658 if (attr
.max_nr_cpus
== 0) {
660 attr
.max_nr_cpus
= rseq_get_max_nr_cpus();
661 if (attr
.max_nr_cpus
== 0) {
667 case MEMPOOL_TYPE_GLOBAL
:
668 /* Use a 1-cpu pool for global mempool type. */
669 attr
.max_nr_cpus
= 1;
673 attr
.stride
= RSEQ_MEMPOOL_STRIDE
; /* Use default */
674 if (attr
.robust_set
&& !attr
.poison_set
) {
675 attr
.poison_set
= true;
676 attr
.poison
= DEFAULT_POISON_VALUE
;
678 if (item_len
> attr
.stride
|| attr
.stride
< (size_t) rseq_get_page_len() ||
679 !is_pow2(attr
.stride
)) {
684 pool
= calloc(1, sizeof(struct rseq_mempool
));
688 memcpy(&pool
->attr
, &attr
, sizeof(attr
));
689 pthread_mutex_init(&pool
->lock
, NULL
);
690 pool
->item_len
= item_len
;
691 pool
->item_order
= order
;
693 pool
->range_list
= rseq_mempool_range_create(pool
);
694 if (!pool
->range_list
)
698 pool
->name
= strdup(pool_name
);
705 rseq_mempool_destroy(pool
);
710 /* Always inline for __builtin_return_address(0). */
711 static inline __attribute__((always_inline
))
712 void set_alloc_slot(struct rseq_mempool
*pool
, struct rseq_mempool_range
*range
, size_t item_offset
)
714 unsigned long *bitmap
= range
->alloc_bitmap
;
715 size_t item_index
= item_offset
>> pool
->item_order
;
722 k
= item_index
/ BIT_PER_ULONG
;
723 mask
= 1ULL << (item_index
% BIT_PER_ULONG
);
725 /* Print error if bit is already set. */
726 if (bitmap
[k
] & mask
) {
727 fprintf(stderr
, "%s: Allocator corruption detected for pool: \"%s\" (%p), item offset: %zu, caller: %p.\n",
728 __func__
, get_pool_name(pool
), pool
, item_offset
, (void *) __builtin_return_address(0));
735 void __rseq_percpu
*__rseq_percpu_malloc(struct rseq_mempool
*pool
,
736 bool zeroed
, void *init_ptr
, size_t init_len
)
738 struct rseq_mempool_range
*range
;
739 struct free_list_node
*node
;
740 uintptr_t item_offset
;
741 void __rseq_percpu
*addr
;
743 if (init_len
> pool
->item_len
) {
747 pthread_mutex_lock(&pool
->lock
);
748 /* Get first entry from free list. */
749 node
= pool
->free_list_head
;
751 uintptr_t ptr
= (uintptr_t) node
;
752 void *range_base
= (void *) (ptr
& (~(pool
->attr
.stride
- 1)));
754 range
= (struct rseq_mempool_range
*) (range_base
- RANGE_HEADER_OFFSET
);
755 /* Remove node from free list (update head). */
756 pool
->free_list_head
= node
->next
;
757 item_offset
= (uintptr_t) ((void *) node
- range_base
);
758 rseq_percpu_check_poison_item(pool
, range
, item_offset
);
759 addr
= (void __rseq_percpu
*) node
;
763 * If the most recent range (first in list) does not have any
764 * room left, create a new range and prepend it to the list
767 range
= pool
->range_list
;
768 if (range
->next_unused
+ pool
->item_len
> pool
->attr
.stride
) {
769 range
= rseq_mempool_range_create(pool
);
775 /* Add range to head of list. */
776 range
->next
= pool
->range_list
;
777 pool
->range_list
= range
;
779 /* First range in list has room left. */
780 item_offset
= range
->next_unused
;
781 addr
= (void __rseq_percpu
*) (range
->base
+ item_offset
);
782 range
->next_unused
+= pool
->item_len
;
785 set_alloc_slot(pool
, range
, item_offset
);
786 pthread_mutex_unlock(&pool
->lock
);
789 rseq_percpu_zero_item(pool
, range
, item_offset
);
791 rseq_percpu_init_item(pool
, range
, item_offset
,
798 void __rseq_percpu
*rseq_mempool_percpu_malloc(struct rseq_mempool
*pool
)
800 return __rseq_percpu_malloc(pool
, false, NULL
, 0);
803 void __rseq_percpu
*rseq_mempool_percpu_zmalloc(struct rseq_mempool
*pool
)
805 return __rseq_percpu_malloc(pool
, true, NULL
, 0);
808 void __rseq_percpu
*rseq_mempool_percpu_malloc_init(struct rseq_mempool
*pool
,
809 void *init_ptr
, size_t len
)
811 return __rseq_percpu_malloc(pool
, false, init_ptr
, len
);
814 /* Always inline for __builtin_return_address(0). */
815 static inline __attribute__((always_inline
))
816 void clear_alloc_slot(struct rseq_mempool
*pool
, struct rseq_mempool_range
*range
, size_t item_offset
)
818 unsigned long *bitmap
= range
->alloc_bitmap
;
819 size_t item_index
= item_offset
>> pool
->item_order
;
826 k
= item_index
/ BIT_PER_ULONG
;
827 mask
= 1ULL << (item_index
% BIT_PER_ULONG
);
829 /* Print error if bit is not set. */
830 if (!(bitmap
[k
] & mask
)) {
831 fprintf(stderr
, "%s: Double-free detected for pool: \"%s\" (%p), item offset: %zu, caller: %p.\n",
832 __func__
, get_pool_name(pool
), pool
, item_offset
,
833 (void *) __builtin_return_address(0));
839 void librseq_mempool_percpu_free(void __rseq_percpu
*_ptr
, size_t stride
)
841 uintptr_t ptr
= (uintptr_t) _ptr
;
842 void *range_base
= (void *) (ptr
& (~(stride
- 1)));
843 struct rseq_mempool_range
*range
= (struct rseq_mempool_range
*) (range_base
- RANGE_HEADER_OFFSET
);
844 struct rseq_mempool
*pool
= range
->pool
;
845 uintptr_t item_offset
= ptr
& (stride
- 1);
846 struct free_list_node
*head
, *item
;
848 pthread_mutex_lock(&pool
->lock
);
849 clear_alloc_slot(pool
, range
, item_offset
);
850 /* Add ptr to head of free list */
851 head
= pool
->free_list_head
;
852 if (pool
->attr
.poison_set
)
853 rseq_percpu_poison_item(pool
, range
, item_offset
);
854 /* Free-list is in CPU 0 range. */
855 item
= (struct free_list_node
*) ptr
;
857 * Setting the next pointer will overwrite the first uintptr_t
861 pool
->free_list_head
= item
;
862 pthread_mutex_unlock(&pool
->lock
);
865 struct rseq_mempool_set
*rseq_mempool_set_create(void)
867 struct rseq_mempool_set
*pool_set
;
869 pool_set
= calloc(1, sizeof(struct rseq_mempool_set
));
872 pthread_mutex_init(&pool_set
->lock
, NULL
);
876 int rseq_mempool_set_destroy(struct rseq_mempool_set
*pool_set
)
880 for (order
= POOL_SET_MIN_ENTRY
; order
< POOL_SET_NR_ENTRIES
; order
++) {
881 struct rseq_mempool
*pool
= pool_set
->entries
[order
];
885 ret
= rseq_mempool_destroy(pool
);
888 pool_set
->entries
[order
] = NULL
;
890 pthread_mutex_destroy(&pool_set
->lock
);
895 /* Ownership of pool is handed over to pool set on success. */
896 int rseq_mempool_set_add_pool(struct rseq_mempool_set
*pool_set
, struct rseq_mempool
*pool
)
898 size_t item_order
= pool
->item_order
;
901 pthread_mutex_lock(&pool_set
->lock
);
902 if (pool_set
->entries
[item_order
]) {
907 pool_set
->entries
[pool
->item_order
] = pool
;
909 pthread_mutex_unlock(&pool_set
->lock
);
914 void __rseq_percpu
*__rseq_mempool_set_malloc(struct rseq_mempool_set
*pool_set
,
915 void *init_ptr
, size_t len
, bool zeroed
)
917 int order
, min_order
= POOL_SET_MIN_ENTRY
;
918 struct rseq_mempool
*pool
;
919 void __rseq_percpu
*addr
;
921 order
= rseq_get_count_order_ulong(len
);
922 if (order
> POOL_SET_MIN_ENTRY
)
925 pthread_mutex_lock(&pool_set
->lock
);
926 /* First smallest present pool where @len fits. */
927 for (order
= min_order
; order
< POOL_SET_NR_ENTRIES
; order
++) {
928 pool
= pool_set
->entries
[order
];
932 if (pool
->item_len
>= len
)
937 pthread_mutex_unlock(&pool_set
->lock
);
939 addr
= __rseq_percpu_malloc(pool
, zeroed
, init_ptr
, len
);
940 if (addr
== NULL
&& errno
== ENOMEM
) {
942 * If the allocation failed, try again with a
945 min_order
= order
+ 1;
956 void __rseq_percpu
*rseq_mempool_set_percpu_malloc(struct rseq_mempool_set
*pool_set
, size_t len
)
958 return __rseq_mempool_set_malloc(pool_set
, NULL
, len
, false);
961 void __rseq_percpu
*rseq_mempool_set_percpu_zmalloc(struct rseq_mempool_set
*pool_set
, size_t len
)
963 return __rseq_mempool_set_malloc(pool_set
, NULL
, len
, true);
966 void __rseq_percpu
*rseq_mempool_set_percpu_malloc_init(struct rseq_mempool_set
*pool_set
,
967 void *init_ptr
, size_t len
)
969 return __rseq_mempool_set_malloc(pool_set
, init_ptr
, len
, true);
972 struct rseq_mempool_attr
*rseq_mempool_attr_create(void)
974 return calloc(1, sizeof(struct rseq_mempool_attr
));
977 void rseq_mempool_attr_destroy(struct rseq_mempool_attr
*attr
)
982 int rseq_mempool_attr_set_mmap(struct rseq_mempool_attr
*attr
,
983 void *(*mmap_func
)(void *priv
, size_t len
),
984 int (*munmap_func
)(void *priv
, void *ptr
, size_t len
),
991 attr
->mmap_set
= true;
992 attr
->mmap_func
= mmap_func
;
993 attr
->munmap_func
= munmap_func
;
994 attr
->mmap_priv
= mmap_priv
;
998 int rseq_mempool_attr_set_init(struct rseq_mempool_attr
*attr
,
999 int (*init_func
)(void *priv
, void *addr
, size_t len
, int cpu
),
1006 attr
->init_set
= true;
1007 attr
->init_func
= init_func
;
1008 attr
->init_priv
= init_priv
;
1012 int rseq_mempool_attr_set_robust(struct rseq_mempool_attr
*attr
)
1018 attr
->robust_set
= true;
1022 int rseq_mempool_attr_set_percpu(struct rseq_mempool_attr
*attr
,
1023 size_t stride
, int max_nr_cpus
)
1029 attr
->type
= MEMPOOL_TYPE_PERCPU
;
1030 attr
->stride
= stride
;
1031 attr
->max_nr_cpus
= max_nr_cpus
;
1035 int rseq_mempool_attr_set_global(struct rseq_mempool_attr
*attr
,
1042 attr
->type
= MEMPOOL_TYPE_GLOBAL
;
1043 attr
->stride
= stride
;
1044 attr
->max_nr_cpus
= 0;
1048 int rseq_mempool_attr_set_max_nr_ranges(struct rseq_mempool_attr
*attr
,
1049 unsigned long max_nr_ranges
)
1055 attr
->max_nr_ranges
= max_nr_ranges
;
1059 int rseq_mempool_attr_set_poison(struct rseq_mempool_attr
*attr
,
1066 attr
->poison_set
= true;
1067 attr
->poison
= poison
;
1071 int rseq_mempool_get_max_nr_cpus(struct rseq_mempool
*mempool
)
1073 if (!mempool
|| mempool
->attr
.type
!= MEMPOOL_TYPE_PERCPU
) {
1077 return mempool
->attr
.max_nr_cpus
;
This page took 0.051852 seconds and 3 git commands to generate.