1 | #define JEMALLOC_BASE_C_ |
2 | #include "jemalloc/internal/jemalloc_internal.h" |
3 | |
4 | /******************************************************************************/ |
5 | /* Data. */ |
6 | |
7 | static malloc_mutex_t base_mtx; |
8 | static extent_tree_t base_avail_szad; |
9 | static extent_node_t *base_nodes; |
10 | static size_t base_allocated; |
11 | static size_t base_resident; |
12 | static size_t base_mapped; |
13 | |
14 | /******************************************************************************/ |
15 | |
16 | static extent_node_t * |
17 | base_node_try_alloc(tsdn_t *tsdn) |
18 | { |
19 | extent_node_t *node; |
20 | |
21 | malloc_mutex_assert_owner(tsdn, &base_mtx); |
22 | |
23 | if (base_nodes == NULL) |
24 | return (NULL); |
25 | node = base_nodes; |
26 | base_nodes = *(extent_node_t **)node; |
27 | JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t)); |
28 | return (node); |
29 | } |
30 | |
31 | static void |
32 | base_node_dalloc(tsdn_t *tsdn, extent_node_t *node) |
33 | { |
34 | |
35 | malloc_mutex_assert_owner(tsdn, &base_mtx); |
36 | |
37 | JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t)); |
38 | *(extent_node_t **)node = base_nodes; |
39 | base_nodes = node; |
40 | } |
41 | |
42 | static extent_node_t * |
43 | base_chunk_alloc(tsdn_t *tsdn, size_t minsize) |
44 | { |
45 | extent_node_t *node; |
46 | size_t csize, nsize; |
47 | void *addr; |
48 | |
49 | malloc_mutex_assert_owner(tsdn, &base_mtx); |
50 | assert(minsize != 0); |
51 | node = base_node_try_alloc(tsdn); |
52 | /* Allocate enough space to also carve a node out if necessary. */ |
53 | nsize = (node == NULL) ? CACHELINE_CEILING(sizeof(extent_node_t)) : 0; |
54 | csize = CHUNK_CEILING(minsize + nsize); |
55 | addr = chunk_alloc_base(csize); |
56 | if (addr == NULL) { |
57 | if (node != NULL) |
58 | base_node_dalloc(tsdn, node); |
59 | return (NULL); |
60 | } |
61 | base_mapped += csize; |
62 | if (node == NULL) { |
63 | node = (extent_node_t *)addr; |
64 | addr = (void *)((uintptr_t)addr + nsize); |
65 | csize -= nsize; |
66 | if (config_stats) { |
67 | base_allocated += nsize; |
68 | base_resident += PAGE_CEILING(nsize); |
69 | } |
70 | } |
71 | extent_node_init(node, NULL, addr, csize, true, true); |
72 | return (node); |
73 | } |
74 | |
75 | /* |
76 | * base_alloc() guarantees demand-zeroed memory, in order to make multi-page |
77 | * sparse data structures such as radix tree nodes efficient with respect to |
78 | * physical memory usage. |
79 | */ |
80 | void * |
81 | base_alloc(tsdn_t *tsdn, size_t size) |
82 | { |
83 | void *ret; |
84 | size_t csize, usize; |
85 | extent_node_t *node; |
86 | extent_node_t key; |
87 | |
88 | /* |
89 | * Round size up to nearest multiple of the cacheline size, so that |
90 | * there is no chance of false cache line sharing. |
91 | */ |
92 | csize = CACHELINE_CEILING(size); |
93 | |
94 | usize = s2u(csize); |
95 | extent_node_init(&key, NULL, NULL, usize, false, false); |
96 | malloc_mutex_lock(tsdn, &base_mtx); |
97 | node = extent_tree_szad_nsearch(&base_avail_szad, &key); |
98 | if (node != NULL) { |
99 | /* Use existing space. */ |
100 | extent_tree_szad_remove(&base_avail_szad, node); |
101 | } else { |
102 | /* Try to allocate more space. */ |
103 | node = base_chunk_alloc(tsdn, csize); |
104 | } |
105 | if (node == NULL) { |
106 | ret = NULL; |
107 | goto label_return; |
108 | } |
109 | |
110 | ret = extent_node_addr_get(node); |
111 | if (extent_node_size_get(node) > csize) { |
112 | extent_node_addr_set(node, (void *)((uintptr_t)ret + csize)); |
113 | extent_node_size_set(node, extent_node_size_get(node) - csize); |
114 | extent_tree_szad_insert(&base_avail_szad, node); |
115 | } else |
116 | base_node_dalloc(tsdn, node); |
117 | if (config_stats) { |
118 | base_allocated += csize; |
119 | /* |
120 | * Add one PAGE to base_resident for every page boundary that is |
121 | * crossed by the new allocation. |
122 | */ |
123 | base_resident += PAGE_CEILING((uintptr_t)ret + csize) - |
124 | PAGE_CEILING((uintptr_t)ret); |
125 | } |
126 | JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, csize); |
127 | label_return: |
128 | malloc_mutex_unlock(tsdn, &base_mtx); |
129 | return (ret); |
130 | } |
131 | |
132 | void |
133 | base_stats_get(tsdn_t *tsdn, size_t *allocated, size_t *resident, |
134 | size_t *mapped) |
135 | { |
136 | |
137 | malloc_mutex_lock(tsdn, &base_mtx); |
138 | assert(base_allocated <= base_resident); |
139 | assert(base_resident <= base_mapped); |
140 | *allocated = base_allocated; |
141 | *resident = base_resident; |
142 | *mapped = base_mapped; |
143 | malloc_mutex_unlock(tsdn, &base_mtx); |
144 | } |
145 | |
146 | bool |
147 | base_boot(void) |
148 | { |
149 | |
150 | if (malloc_mutex_init(&base_mtx, "base" , WITNESS_RANK_BASE)) |
151 | return (true); |
152 | extent_tree_szad_new(&base_avail_szad); |
153 | base_nodes = NULL; |
154 | |
155 | return (false); |
156 | } |
157 | |
158 | void |
159 | base_prefork(tsdn_t *tsdn) |
160 | { |
161 | |
162 | malloc_mutex_prefork(tsdn, &base_mtx); |
163 | } |
164 | |
165 | void |
166 | base_postfork_parent(tsdn_t *tsdn) |
167 | { |
168 | |
169 | malloc_mutex_postfork_parent(tsdn, &base_mtx); |
170 | } |
171 | |
172 | void |
173 | base_postfork_child(tsdn_t *tsdn) |
174 | { |
175 | |
176 | malloc_mutex_postfork_child(tsdn, &base_mtx); |
177 | } |
178 | |