| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "judy-malloc.h" |
| 4 | |
| 5 | // -------------------------------------------------------------------------------------------------------------------- |
| 6 | // Judy using ARAL |
| 7 | |
| 8 | #define MAX_JUDY_SIZE_TO_ARAL 24 |
| 9 | static bool judy_sizes_config[MAX_JUDY_SIZE_TO_ARAL + 1] = { |
| 10 | [3] = true, |
| 11 | [4] = true, |
| 12 | [5] = true, |
| 13 | [6] = true, |
| 14 | [7] = true, |
| 15 | [8] = true, |
| 16 | [10] = true, |
| 17 | [11] = true, |
| 18 | [15] = true, |
| 19 | [23] = true, |
| 20 | }; |
| 21 | static ARAL *judy_sizes_aral[MAX_JUDY_SIZE_TO_ARAL + 1] = { 0 }; |
| 22 | |
| 23 | struct aral_statistics judy_sizes_aral_statistics = { 0 }; |
| 24 | |
| 25 | static void aral_judy_init(void) { |
| 26 | for(size_t Words = 0; Words <= MAX_JUDY_SIZE_TO_ARAL; Words++) |
| 27 | if(judy_sizes_config[Words]) { |
| 28 | char buf[30+1]; |
| 29 | snprintfz(buf, sizeof(buf) - 1, "judy-%zu", Words * sizeof(Word_t)); |
| 30 | judy_sizes_aral[Words] = aral_create( |
| 31 | buf, |
| 32 | Words * sizeof(Word_t), |
| 33 | 0, |
| 34 | 0, |
| 35 | &judy_sizes_aral_statistics, |
| 36 | NULL, NULL, false, false, false); |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | size_t judy_aral_free_bytes(void) { |
| 41 | return aral_free_bytes_from_stats(&judy_sizes_aral_statistics); |
| 42 | } |
| 43 | |
| 44 | size_t judy_aral_structures(void) { |
| 45 | return aral_structures_bytes_from_stats(&judy_sizes_aral_statistics); |
| 46 | } |
| 47 | |
| 48 | struct aral_statistics *judy_aral_statistics(void) { |
| 49 | return &judy_sizes_aral_statistics; |
| 50 | } |
| 51 | |
| 52 | static ARAL *judy_size_aral(Word_t Words) { |
| 53 | if(Words <= MAX_JUDY_SIZE_TO_ARAL) |
| 54 | return judy_sizes_aral[Words]; |
| 55 | |
| 56 | return NULL; |
| 57 | } |
| 58 | |
| 59 | // -------------------------------------------------------------------------------------------------------------------- |
| 60 | // Judy memory tracking |
| 61 | |
| 62 | static __thread int64_t judy_allocated = 0; |
| 63 | |
| 64 | ALWAYS_INLINE void JudyAllocThreadPulseReset(void) { |
| 65 | judy_allocated = 0; |
| 66 | } |
| 67 | |
| 68 | ALWAYS_INLINE int64_t JudyAllocThreadPulseGetAndReset(void) { |
| 69 | int64_t rc = judy_allocated; |
| 70 | judy_allocated = 0; |
| 71 | return rc; |
| 72 | } |
| 73 | |
| 74 | // -------------------------------------------------------------------------------------------------------------------- |
| 75 | // Judy dedicated jemalloc arena |
| 76 | |
| 77 | static unsigned jemalloc_arena_index __maybe_unused = 0; |
| 78 | static bool jemalloc_initialized __maybe_unused = false; |
| 79 | |
| 80 | #ifdef HAVE_JEMALLOC_ARENA_API |
| 81 | #include <jemalloc/jemalloc.h> |
| 82 | static void jemalloc_init(void) { |
| 83 | // Create shared arena |
| 84 | size_t sz = sizeof(unsigned); |
| 85 | if (mallctl("arenas.create", &jemalloc_arena_index, &sz, NULL, 0) != 0) |
| 86 | return; |
| 87 | |
| 88 | // Disable thread cache for direct arena access |
| 89 | int cache_enabled = 0; |
| 90 | if (mallctl("thread.tcache.enabled", NULL, NULL, &cache_enabled, sizeof(bool)) != 0) |
| 91 | return; |
| 92 | |
| 93 | jemalloc_initialized = true; |
| 94 | } |
| 95 | |
| 96 | static void *jemalloc_malloc(Word_t Words) { |
| 97 | return mallocx(Words * sizeof(Word_t), MALLOCX_ARENA(jemalloc_arena_index)); |
| 98 | } |
| 99 | |
| 100 | static void jemalloc_free(void * PWord, Word_t Words __maybe_unused) { |
| 101 | if(PWord) |
| 102 | dallocx(PWord, MALLOCX_ARENA(jemalloc_arena_index)); |
| 103 | } |
| 104 | #endif |
| 105 | |
| 106 | // -------------------------------------------------------------------------------------------------------------------- |
| 107 | // Judy API |
| 108 | |
| 109 | inline Word_t JudyMalloc(Word_t Words) |
| 110 | { |
| 111 | Word_t Addr; |
| 112 | |
| 113 | #ifdef HAVE_JEMALLOC_ARENA_API |
| 114 | if(jemalloc_initialized) |
| 115 | Addr = (Word_t)jemalloc_malloc(Words); |
| 116 | else |
| 117 | #endif |
| 118 | { |
| 119 | ARAL *ar = judy_size_aral(Words); |
| 120 | if (ar) |
| 121 | Addr = (Word_t)aral_mallocz(ar); |
| 122 | else |
| 123 | Addr = (Word_t)mallocz(Words * sizeof(Word_t)); |
| 124 | } |
| 125 | |
| 126 | judy_allocated += Words * sizeof(Word_t); |
| 127 | |
| 128 | return(Addr); |
| 129 | } |
| 130 | |
| 131 | inline void JudyFree(void * PWord, Word_t Words) { |
| 132 | #ifdef HAVE_JEMALLOC_ARENA_API |
| 133 | if(jemalloc_initialized) |
| 134 | jemalloc_free(PWord, Words); |
| 135 | else |
| 136 | #endif |
| 137 | { |
| 138 | ARAL *ar = judy_size_aral(Words); |
| 139 | if (ar) |
| 140 | aral_freez(ar, PWord); |
| 141 | else |
| 142 | freez(PWord); |
| 143 | } |
| 144 | |
| 145 | judy_allocated -= Words * sizeof(Word_t); |
| 146 | } |
| 147 | |
| 148 | Word_t JudyMallocVirtual(Word_t Words) { |
| 149 | return JudyMalloc(Words); |
| 150 | } |
| 151 | |
| 152 | void JudyFreeVirtual(void * PWord, Word_t Words) { |
| 153 | JudyFree(PWord, Words); |
| 154 | } |
| 155 | |
| 156 | // -------------------------------------------------------------------------------------------------------------------- |
| 157 | // initialization |
| 158 | |
| 159 | void libjudy_malloc_init(void) { |
| 160 | // IMPORTANT: this is not called on external plugins |
| 161 | // the allocator should run even if this is not called |
| 162 | |
| 163 | (void)jemalloc_initialized; |
| 164 | (void)jemalloc_arena_index; |
| 165 | |
| 166 | #ifdef HAVE_JEMALLOC_ARENA_API |
| 167 | jemalloc_init(); |
| 168 | if(!jemalloc_initialized) |
| 169 | #endif |
| 170 | aral_judy_init(); |
| 171 | } |
| 172 |