xref: /redis-3.2.3/deps/jemalloc/src/huge.c (revision 82154ffa)
1 #define	JEMALLOC_HUGE_C_
2 #include "jemalloc/internal/jemalloc_internal.h"
3 
4 /******************************************************************************/
5 /* Data. */
6 
7 uint64_t	huge_nmalloc;
8 uint64_t	huge_ndalloc;
9 size_t		huge_allocated;
10 
11 malloc_mutex_t	huge_mtx;
12 
13 /******************************************************************************/
14 
15 /* Tree of chunks that are stand-alone huge allocations. */
16 static extent_tree_t	huge;
17 
18 void *
19 huge_malloc(size_t size, bool zero, dss_prec_t dss_prec)
20 {
21 
22 	return (huge_palloc(size, chunksize, zero, dss_prec));
23 }
24 
25 void *
26 huge_palloc(size_t size, size_t alignment, bool zero, dss_prec_t dss_prec)
27 {
28 	void *ret;
29 	size_t csize;
30 	extent_node_t *node;
31 	bool is_zeroed;
32 
33 	/* Allocate one or more contiguous chunks for this request. */
34 
35 	csize = CHUNK_CEILING(size);
36 	if (csize == 0) {
37 		/* size is large enough to cause size_t wrap-around. */
38 		return (NULL);
39 	}
40 
41 	/* Allocate an extent node with which to track the chunk. */
42 	node = base_node_alloc();
43 	if (node == NULL)
44 		return (NULL);
45 
46 	/*
47 	 * Copy zero into is_zeroed and pass the copy to chunk_alloc(), so that
48 	 * it is possible to make correct junk/zero fill decisions below.
49 	 */
50 	is_zeroed = zero;
51 	ret = chunk_alloc(csize, alignment, false, &is_zeroed, dss_prec);
52 	if (ret == NULL) {
53 		base_node_dealloc(node);
54 		return (NULL);
55 	}
56 
57 	/* Insert node into huge. */
58 	node->addr = ret;
59 	node->size = csize;
60 
61 	malloc_mutex_lock(&huge_mtx);
62 	extent_tree_ad_insert(&huge, node);
63 	if (config_stats) {
64 		stats_cactive_add(csize);
65 		huge_nmalloc++;
66 		huge_allocated += csize;
67 	}
68 	malloc_mutex_unlock(&huge_mtx);
69 
70 	if (config_fill && zero == false) {
71 		if (opt_junk)
72 			memset(ret, 0xa5, csize);
73 		else if (opt_zero && is_zeroed == false)
74 			memset(ret, 0, csize);
75 	}
76 
77 	return (ret);
78 }
79 
80 bool
81 huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra)
82 {
83 
84 	/*
85 	 * Avoid moving the allocation if the size class can be left the same.
86 	 */
87 	if (oldsize > arena_maxclass
88 	    && CHUNK_CEILING(oldsize) >= CHUNK_CEILING(size)
89 	    && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(size+extra)) {
90 		assert(CHUNK_CEILING(oldsize) == oldsize);
91 		return (false);
92 	}
93 
94 	/* Reallocation would require a move. */
95 	return (true);
96 }
97 
98 void *
99 huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
100     size_t alignment, bool zero, bool try_tcache_dalloc, dss_prec_t dss_prec)
101 {
102 	void *ret;
103 	size_t copysize;
104 
105 	/* Try to avoid moving the allocation. */
106 	if (huge_ralloc_no_move(ptr, oldsize, size, extra) == false)
107 		return (ptr);
108 
109 	/*
110 	 * size and oldsize are different enough that we need to use a
111 	 * different size class.  In that case, fall back to allocating new
112 	 * space and copying.
113 	 */
114 	if (alignment > chunksize)
115 		ret = huge_palloc(size + extra, alignment, zero, dss_prec);
116 	else
117 		ret = huge_malloc(size + extra, zero, dss_prec);
118 
119 	if (ret == NULL) {
120 		if (extra == 0)
121 			return (NULL);
122 		/* Try again, this time without extra. */
123 		if (alignment > chunksize)
124 			ret = huge_palloc(size, alignment, zero, dss_prec);
125 		else
126 			ret = huge_malloc(size, zero, dss_prec);
127 
128 		if (ret == NULL)
129 			return (NULL);
130 	}
131 
132 	/*
133 	 * Copy at most size bytes (not size+extra), since the caller has no
134 	 * expectation that the extra bytes will be reliably preserved.
135 	 */
136 	copysize = (size < oldsize) ? size : oldsize;
137 
138 #ifdef JEMALLOC_MREMAP
139 	/*
140 	 * Use mremap(2) if this is a huge-->huge reallocation, and neither the
141 	 * source nor the destination are in dss.
142 	 */
143 	if (oldsize >= chunksize && (config_dss == false || (chunk_in_dss(ptr)
144 	    == false && chunk_in_dss(ret) == false))) {
145 		size_t newsize = huge_salloc(ret);
146 
147 		/*
148 		 * Remove ptr from the tree of huge allocations before
149 		 * performing the remap operation, in order to avoid the
150 		 * possibility of another thread acquiring that mapping before
151 		 * this one removes it from the tree.
152 		 */
153 		huge_dalloc(ptr, false);
154 		if (mremap(ptr, oldsize, newsize, MREMAP_MAYMOVE|MREMAP_FIXED,
155 		    ret) == MAP_FAILED) {
156 			/*
157 			 * Assuming no chunk management bugs in the allocator,
158 			 * the only documented way an error can occur here is
159 			 * if the application changed the map type for a
160 			 * portion of the old allocation.  This is firmly in
161 			 * undefined behavior territory, so write a diagnostic
162 			 * message, and optionally abort.
163 			 */
164 			char buf[BUFERROR_BUF];
165 
166 			buferror(get_errno(), buf, sizeof(buf));
167 			malloc_printf("<jemalloc>: Error in mremap(): %s\n",
168 			    buf);
169 			if (opt_abort)
170 				abort();
171 			memcpy(ret, ptr, copysize);
172 			chunk_dealloc_mmap(ptr, oldsize);
173 		} else if (config_fill && zero == false && opt_junk && oldsize
174 		    < newsize) {
175 			/*
176 			 * mremap(2) clobbers the original mapping, so
177 			 * junk/zero filling is not preserved.  There is no
178 			 * need to zero fill here, since any trailing
179 			 * uninititialized memory is demand-zeroed by the
180 			 * kernel, but junk filling must be redone.
181 			 */
182 			memset(ret + oldsize, 0xa5, newsize - oldsize);
183 		}
184 	} else
185 #endif
186 	{
187 		memcpy(ret, ptr, copysize);
188 		iqalloct(ptr, try_tcache_dalloc);
189 	}
190 	return (ret);
191 }
192 
193 #ifdef JEMALLOC_JET
194 #undef huge_dalloc_junk
195 #define	huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk_impl)
196 #endif
197 static void
198 huge_dalloc_junk(void *ptr, size_t usize)
199 {
200 
201 	if (config_fill && config_dss && opt_junk) {
202 		/*
203 		 * Only bother junk filling if the chunk isn't about to be
204 		 * unmapped.
205 		 */
206 		if (config_munmap == false || (config_dss && chunk_in_dss(ptr)))
207 			memset(ptr, 0x5a, usize);
208 	}
209 }
210 #ifdef JEMALLOC_JET
211 #undef huge_dalloc_junk
212 #define	huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk)
213 huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl);
214 #endif
215 
216 void
217 huge_dalloc(void *ptr, bool unmap)
218 {
219 	extent_node_t *node, key;
220 
221 	malloc_mutex_lock(&huge_mtx);
222 
223 	/* Extract from tree of huge allocations. */
224 	key.addr = ptr;
225 	node = extent_tree_ad_search(&huge, &key);
226 	assert(node != NULL);
227 	assert(node->addr == ptr);
228 	extent_tree_ad_remove(&huge, node);
229 
230 	if (config_stats) {
231 		stats_cactive_sub(node->size);
232 		huge_ndalloc++;
233 		huge_allocated -= node->size;
234 	}
235 
236 	malloc_mutex_unlock(&huge_mtx);
237 
238 	if (unmap)
239 		huge_dalloc_junk(node->addr, node->size);
240 
241 	chunk_dealloc(node->addr, node->size, unmap);
242 
243 	base_node_dealloc(node);
244 }
245 
246 size_t
247 huge_salloc(const void *ptr)
248 {
249 	size_t ret;
250 	extent_node_t *node, key;
251 
252 	malloc_mutex_lock(&huge_mtx);
253 
254 	/* Extract from tree of huge allocations. */
255 	key.addr = __DECONST(void *, ptr);
256 	node = extent_tree_ad_search(&huge, &key);
257 	assert(node != NULL);
258 
259 	ret = node->size;
260 
261 	malloc_mutex_unlock(&huge_mtx);
262 
263 	return (ret);
264 }
265 
266 dss_prec_t
267 huge_dss_prec_get(arena_t *arena)
268 {
269 
270 	return (arena_dss_prec_get(choose_arena(arena)));
271 }
272 
273 prof_ctx_t *
274 huge_prof_ctx_get(const void *ptr)
275 {
276 	prof_ctx_t *ret;
277 	extent_node_t *node, key;
278 
279 	malloc_mutex_lock(&huge_mtx);
280 
281 	/* Extract from tree of huge allocations. */
282 	key.addr = __DECONST(void *, ptr);
283 	node = extent_tree_ad_search(&huge, &key);
284 	assert(node != NULL);
285 
286 	ret = node->prof_ctx;
287 
288 	malloc_mutex_unlock(&huge_mtx);
289 
290 	return (ret);
291 }
292 
293 void
294 huge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx)
295 {
296 	extent_node_t *node, key;
297 
298 	malloc_mutex_lock(&huge_mtx);
299 
300 	/* Extract from tree of huge allocations. */
301 	key.addr = __DECONST(void *, ptr);
302 	node = extent_tree_ad_search(&huge, &key);
303 	assert(node != NULL);
304 
305 	node->prof_ctx = ctx;
306 
307 	malloc_mutex_unlock(&huge_mtx);
308 }
309 
310 bool
311 huge_boot(void)
312 {
313 
314 	/* Initialize chunks data. */
315 	if (malloc_mutex_init(&huge_mtx))
316 		return (true);
317 	extent_tree_ad_new(&huge);
318 
319 	if (config_stats) {
320 		huge_nmalloc = 0;
321 		huge_ndalloc = 0;
322 		huge_allocated = 0;
323 	}
324 
325 	return (false);
326 }
327 
328 void
329 huge_prefork(void)
330 {
331 
332 	malloc_mutex_prefork(&huge_mtx);
333 }
334 
335 void
336 huge_postfork_parent(void)
337 {
338 
339 	malloc_mutex_postfork_parent(&huge_mtx);
340 }
341 
342 void
343 huge_postfork_child(void)
344 {
345 
346 	malloc_mutex_postfork_child(&huge_mtx);
347 }
348