xref: /freebsd-src/contrib/jemalloc/src/pages.c (revision 59e013a52c22624be0f4b0f9bfe0918bc6f50b93)
1d0e79aa3SJason Evans #define JEMALLOC_PAGES_C_
2b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_preamble.h"
3b7eaed25SJason Evans 
4b7eaed25SJason Evans #include "jemalloc/internal/pages.h"
5b7eaed25SJason Evans 
6b7eaed25SJason Evans #include "jemalloc/internal/jemalloc_internal_includes.h"
7b7eaed25SJason Evans 
8b7eaed25SJason Evans #include "jemalloc/internal/assert.h"
9b7eaed25SJason Evans #include "jemalloc/internal/malloc_io.h"
10d0e79aa3SJason Evans 
111f0a49e8SJason Evans #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT
121f0a49e8SJason Evans #include <sys/sysctl.h>
130ef50b4eSJason Evans #ifdef __FreeBSD__
14*0ae364adSKonstantin Belousov #include <sys/auxv.h>
150ef50b4eSJason Evans #include <vm/vm_param.h>
1687384c51SKonstantin Belousov #include <vm/vm.h>
170ef50b4eSJason Evans #endif
181f0a49e8SJason Evans #endif
191f0a49e8SJason Evans 
201f0a49e8SJason Evans /******************************************************************************/
211f0a49e8SJason Evans /* Data. */
221f0a49e8SJason Evans 
23b7eaed25SJason Evans /* Actual operating system page size, detected during bootstrap, <= PAGE. */
24b7eaed25SJason Evans static size_t	os_page;
25b7eaed25SJason Evans 
261f0a49e8SJason Evans #ifndef _WIN32
271f0a49e8SJason Evans #  define PAGES_PROT_COMMIT (PROT_READ | PROT_WRITE)
281f0a49e8SJason Evans #  define PAGES_PROT_DECOMMIT (PROT_NONE)
291f0a49e8SJason Evans static int	mmap_flags;
301f0a49e8SJason Evans #endif
311f0a49e8SJason Evans static bool	os_overcommits;
321f0a49e8SJason Evans 
330ef50b4eSJason Evans const char *thp_mode_names[] = {
340ef50b4eSJason Evans 	"default",
350ef50b4eSJason Evans 	"always",
360ef50b4eSJason Evans 	"never",
370ef50b4eSJason Evans 	"not supported"
380ef50b4eSJason Evans };
390ef50b4eSJason Evans thp_mode_t opt_thp = THP_MODE_DEFAULT;
400ef50b4eSJason Evans thp_mode_t init_system_thp_mode;
410ef50b4eSJason Evans 
420ef50b4eSJason Evans /* Runtime support for lazy purge. Irrelevant when !pages_can_purge_lazy. */
430ef50b4eSJason Evans static bool pages_can_purge_lazy_runtime = true;
440ef50b4eSJason Evans 
45d0e79aa3SJason Evans /******************************************************************************/
46b7eaed25SJason Evans /*
47b7eaed25SJason Evans  * Function prototypes for static functions that are referenced prior to
48b7eaed25SJason Evans  * definition.
49b7eaed25SJason Evans  */
50d0e79aa3SJason Evans 
51b7eaed25SJason Evans static void os_pages_unmap(void *addr, size_t size);
52d0e79aa3SJason Evans 
53b7eaed25SJason Evans /******************************************************************************/
54b7eaed25SJason Evans 
55b7eaed25SJason Evans static void *
56b7eaed25SJason Evans os_pages_map(void *addr, size_t size, size_t alignment, bool *commit) {
57b7eaed25SJason Evans 	assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);
58b7eaed25SJason Evans 	assert(ALIGNMENT_CEILING(size, os_page) == size);
59d0e79aa3SJason Evans 	assert(size != 0);
60d0e79aa3SJason Evans 
61b7eaed25SJason Evans 	if (os_overcommits) {
621f0a49e8SJason Evans 		*commit = true;
63b7eaed25SJason Evans 	}
641f0a49e8SJason Evans 
65b7eaed25SJason Evans 	void *ret;
66d0e79aa3SJason Evans #ifdef _WIN32
67d0e79aa3SJason Evans 	/*
68d0e79aa3SJason Evans 	 * If VirtualAlloc can't allocate at the given address when one is
69d0e79aa3SJason Evans 	 * given, it fails and returns NULL.
70d0e79aa3SJason Evans 	 */
711f0a49e8SJason Evans 	ret = VirtualAlloc(addr, size, MEM_RESERVE | (*commit ? MEM_COMMIT : 0),
72d0e79aa3SJason Evans 	    PAGE_READWRITE);
73d0e79aa3SJason Evans #else
74d0e79aa3SJason Evans 	/*
75d0e79aa3SJason Evans 	 * We don't use MAP_FIXED here, because it can cause the *replacement*
76d0e79aa3SJason Evans 	 * of existing mappings, and we only want to create new mappings.
77d0e79aa3SJason Evans 	 */
781f0a49e8SJason Evans 	{
791f0a49e8SJason Evans 		int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;
801f0a49e8SJason Evans 
811f0a49e8SJason Evans 		ret = mmap(addr, size, prot, mmap_flags, -1, 0);
821f0a49e8SJason Evans 	}
83d0e79aa3SJason Evans 	assert(ret != NULL);
84d0e79aa3SJason Evans 
85b7eaed25SJason Evans 	if (ret == MAP_FAILED) {
86d0e79aa3SJason Evans 		ret = NULL;
87b7eaed25SJason Evans 	} else if (addr != NULL && ret != addr) {
88d0e79aa3SJason Evans 		/*
89d0e79aa3SJason Evans 		 * We succeeded in mapping memory, but not in the right place.
90d0e79aa3SJason Evans 		 */
91b7eaed25SJason Evans 		os_pages_unmap(ret, size);
92d0e79aa3SJason Evans 		ret = NULL;
93d0e79aa3SJason Evans 	}
94d0e79aa3SJason Evans #endif
95b7eaed25SJason Evans 	assert(ret == NULL || (addr == NULL && ret != addr) || (addr != NULL &&
96b7eaed25SJason Evans 	    ret == addr));
97b7eaed25SJason Evans 	return ret;
98d0e79aa3SJason Evans }
99d0e79aa3SJason Evans 
100b7eaed25SJason Evans static void *
101b7eaed25SJason Evans os_pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size,
102b7eaed25SJason Evans     bool *commit) {
103b7eaed25SJason Evans 	void *ret = (void *)((uintptr_t)addr + leadsize);
104b7eaed25SJason Evans 
105b7eaed25SJason Evans 	assert(alloc_size >= leadsize + size);
106b7eaed25SJason Evans #ifdef _WIN32
107b7eaed25SJason Evans 	os_pages_unmap(addr, alloc_size);
108b7eaed25SJason Evans 	void *new_addr = os_pages_map(ret, size, PAGE, commit);
109b7eaed25SJason Evans 	if (new_addr == ret) {
110b7eaed25SJason Evans 		return ret;
111b7eaed25SJason Evans 	}
112b7eaed25SJason Evans 	if (new_addr != NULL) {
113b7eaed25SJason Evans 		os_pages_unmap(new_addr, size);
114b7eaed25SJason Evans 	}
115b7eaed25SJason Evans 	return NULL;
116b7eaed25SJason Evans #else
117b7eaed25SJason Evans 	size_t trailsize = alloc_size - leadsize - size;
118b7eaed25SJason Evans 
119b7eaed25SJason Evans 	if (leadsize != 0) {
120b7eaed25SJason Evans 		os_pages_unmap(addr, leadsize);
121b7eaed25SJason Evans 	}
122b7eaed25SJason Evans 	if (trailsize != 0) {
123b7eaed25SJason Evans 		os_pages_unmap((void *)((uintptr_t)ret + size), trailsize);
124b7eaed25SJason Evans 	}
125b7eaed25SJason Evans 	return ret;
126b7eaed25SJason Evans #endif
127b7eaed25SJason Evans }
128b7eaed25SJason Evans 
129b7eaed25SJason Evans static void
130b7eaed25SJason Evans os_pages_unmap(void *addr, size_t size) {
131b7eaed25SJason Evans 	assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);
132b7eaed25SJason Evans 	assert(ALIGNMENT_CEILING(size, os_page) == size);
133d0e79aa3SJason Evans 
134d0e79aa3SJason Evans #ifdef _WIN32
135d0e79aa3SJason Evans 	if (VirtualFree(addr, 0, MEM_RELEASE) == 0)
136d0e79aa3SJason Evans #else
137d0e79aa3SJason Evans 	if (munmap(addr, size) == -1)
138d0e79aa3SJason Evans #endif
139d0e79aa3SJason Evans 	{
140d0e79aa3SJason Evans 		char buf[BUFERROR_BUF];
141d0e79aa3SJason Evans 
142d0e79aa3SJason Evans 		buferror(get_errno(), buf, sizeof(buf));
143d0e79aa3SJason Evans 		malloc_printf("<jemalloc>: Error in "
144d0e79aa3SJason Evans #ifdef _WIN32
145d0e79aa3SJason Evans 		    "VirtualFree"
146d0e79aa3SJason Evans #else
147d0e79aa3SJason Evans 		    "munmap"
148d0e79aa3SJason Evans #endif
149d0e79aa3SJason Evans 		    "(): %s\n", buf);
150b7eaed25SJason Evans 		if (opt_abort) {
151d0e79aa3SJason Evans 			abort();
152d0e79aa3SJason Evans 		}
153d0e79aa3SJason Evans 	}
154b7eaed25SJason Evans }
155b7eaed25SJason Evans 
156b7eaed25SJason Evans static void *
157b7eaed25SJason Evans pages_map_slow(size_t size, size_t alignment, bool *commit) {
158b7eaed25SJason Evans 	size_t alloc_size = size + alignment - os_page;
159b7eaed25SJason Evans 	/* Beware size_t wrap-around. */
160b7eaed25SJason Evans 	if (alloc_size < size) {
161b7eaed25SJason Evans 		return NULL;
162b7eaed25SJason Evans 	}
163b7eaed25SJason Evans 
164b7eaed25SJason Evans 	void *ret;
165b7eaed25SJason Evans 	do {
166b7eaed25SJason Evans 		void *pages = os_pages_map(NULL, alloc_size, alignment, commit);
167b7eaed25SJason Evans 		if (pages == NULL) {
168b7eaed25SJason Evans 			return NULL;
169b7eaed25SJason Evans 		}
170b7eaed25SJason Evans 		size_t leadsize = ALIGNMENT_CEILING((uintptr_t)pages, alignment)
171b7eaed25SJason Evans 		    - (uintptr_t)pages;
172b7eaed25SJason Evans 		ret = os_pages_trim(pages, alloc_size, leadsize, size, commit);
173b7eaed25SJason Evans 	} while (ret == NULL);
174b7eaed25SJason Evans 
175b7eaed25SJason Evans 	assert(ret != NULL);
176b7eaed25SJason Evans 	assert(PAGE_ADDR2BASE(ret) == ret);
177b7eaed25SJason Evans 	return ret;
178b7eaed25SJason Evans }
179d0e79aa3SJason Evans 
180d0e79aa3SJason Evans void *
181b7eaed25SJason Evans pages_map(void *addr, size_t size, size_t alignment, bool *commit) {
182b7eaed25SJason Evans 	assert(alignment >= PAGE);
183b7eaed25SJason Evans 	assert(ALIGNMENT_ADDR2BASE(addr, alignment) == addr);
184d0e79aa3SJason Evans 
1850c885e27SEdward Tomasz Napierala #if defined(__FreeBSD__) && defined(MAP_EXCL)
1860c885e27SEdward Tomasz Napierala 	/*
1870c885e27SEdward Tomasz Napierala 	 * FreeBSD has mechanisms both to mmap at specific address without
1880c885e27SEdward Tomasz Napierala 	 * touching existing mappings, and to mmap with specific alignment.
1890c885e27SEdward Tomasz Napierala 	 */
1900c885e27SEdward Tomasz Napierala 	{
191086165ecSEdward Tomasz Napierala 		if (os_overcommits) {
192086165ecSEdward Tomasz Napierala 			*commit = true;
193086165ecSEdward Tomasz Napierala 		}
194086165ecSEdward Tomasz Napierala 
1950c885e27SEdward Tomasz Napierala 		int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;
1960c885e27SEdward Tomasz Napierala 		int flags = mmap_flags;
1970c885e27SEdward Tomasz Napierala 
1980c885e27SEdward Tomasz Napierala 		if (addr != NULL) {
1990c885e27SEdward Tomasz Napierala 			flags |= MAP_FIXED | MAP_EXCL;
2000c885e27SEdward Tomasz Napierala 		} else {
2010c885e27SEdward Tomasz Napierala 			unsigned alignment_bits = ffs_zu(alignment);
2020c885e27SEdward Tomasz Napierala 			assert(alignment_bits > 1);
2030c885e27SEdward Tomasz Napierala 			flags |= MAP_ALIGNED(alignment_bits - 1);
2040c885e27SEdward Tomasz Napierala 		}
2050c885e27SEdward Tomasz Napierala 
2060c885e27SEdward Tomasz Napierala 		void *ret = mmap(addr, size, prot, flags, -1, 0);
2070c885e27SEdward Tomasz Napierala 		if (ret == MAP_FAILED) {
2080c885e27SEdward Tomasz Napierala 			ret = NULL;
2090c885e27SEdward Tomasz Napierala 		}
2100c885e27SEdward Tomasz Napierala 
2110c885e27SEdward Tomasz Napierala 		return ret;
2120c885e27SEdward Tomasz Napierala 	}
2130c885e27SEdward Tomasz Napierala #endif
214b7eaed25SJason Evans 	/*
215b7eaed25SJason Evans 	 * Ideally, there would be a way to specify alignment to mmap() (like
216b7eaed25SJason Evans 	 * NetBSD has), but in the absence of such a feature, we have to work
217b7eaed25SJason Evans 	 * hard to efficiently create aligned mappings.  The reliable, but
218b7eaed25SJason Evans 	 * slow method is to create a mapping that is over-sized, then trim the
219b7eaed25SJason Evans 	 * excess.  However, that always results in one or two calls to
220b7eaed25SJason Evans 	 * os_pages_unmap(), and it can leave holes in the process's virtual
221b7eaed25SJason Evans 	 * memory map if memory grows downward.
222b7eaed25SJason Evans 	 *
223b7eaed25SJason Evans 	 * Optimistically try mapping precisely the right amount before falling
224b7eaed25SJason Evans 	 * back to the slow method, with the expectation that the optimistic
225b7eaed25SJason Evans 	 * approach works most of the time.
226b7eaed25SJason Evans 	 */
227d0e79aa3SJason Evans 
228b7eaed25SJason Evans 	void *ret = os_pages_map(addr, size, os_page, commit);
229b7eaed25SJason Evans 	if (ret == NULL || ret == addr) {
230b7eaed25SJason Evans 		return ret;
231d0e79aa3SJason Evans 	}
232b7eaed25SJason Evans 	assert(addr == NULL);
233b7eaed25SJason Evans 	if (ALIGNMENT_ADDR2OFFSET(ret, alignment) != 0) {
234b7eaed25SJason Evans 		os_pages_unmap(ret, size);
235b7eaed25SJason Evans 		return pages_map_slow(size, alignment, commit);
236d0e79aa3SJason Evans 	}
237b7eaed25SJason Evans 
238b7eaed25SJason Evans 	assert(PAGE_ADDR2BASE(ret) == ret);
239b7eaed25SJason Evans 	return ret;
240b7eaed25SJason Evans }
241b7eaed25SJason Evans 
242b7eaed25SJason Evans void
243b7eaed25SJason Evans pages_unmap(void *addr, size_t size) {
244b7eaed25SJason Evans 	assert(PAGE_ADDR2BASE(addr) == addr);
245b7eaed25SJason Evans 	assert(PAGE_CEILING(size) == size);
246b7eaed25SJason Evans 
247b7eaed25SJason Evans 	os_pages_unmap(addr, size);
248d0e79aa3SJason Evans }
249d0e79aa3SJason Evans 
250d0e79aa3SJason Evans static bool
251b7eaed25SJason Evans pages_commit_impl(void *addr, size_t size, bool commit) {
252b7eaed25SJason Evans 	assert(PAGE_ADDR2BASE(addr) == addr);
253b7eaed25SJason Evans 	assert(PAGE_CEILING(size) == size);
254d0e79aa3SJason Evans 
255b7eaed25SJason Evans 	if (os_overcommits) {
256b7eaed25SJason Evans 		return true;
257b7eaed25SJason Evans 	}
2581f0a49e8SJason Evans 
2591f0a49e8SJason Evans #ifdef _WIN32
2601f0a49e8SJason Evans 	return (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT,
2611f0a49e8SJason Evans 	    PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT)));
2621f0a49e8SJason Evans #else
2631f0a49e8SJason Evans 	{
2641f0a49e8SJason Evans 		int prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;
2651f0a49e8SJason Evans 		void *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED,
2661f0a49e8SJason Evans 		    -1, 0);
267b7eaed25SJason Evans 		if (result == MAP_FAILED) {
268b7eaed25SJason Evans 			return true;
269b7eaed25SJason Evans 		}
270d0e79aa3SJason Evans 		if (result != addr) {
271d0e79aa3SJason Evans 			/*
272d0e79aa3SJason Evans 			 * We succeeded in mapping memory, but not in the right
273d0e79aa3SJason Evans 			 * place.
274d0e79aa3SJason Evans 			 */
275b7eaed25SJason Evans 			os_pages_unmap(result, size);
276b7eaed25SJason Evans 			return true;
277d0e79aa3SJason Evans 		}
278b7eaed25SJason Evans 		return false;
279d0e79aa3SJason Evans 	}
280d0e79aa3SJason Evans #endif
281d0e79aa3SJason Evans }
282d0e79aa3SJason Evans 
283d0e79aa3SJason Evans bool
284b7eaed25SJason Evans pages_commit(void *addr, size_t size) {
285b7eaed25SJason Evans 	return pages_commit_impl(addr, size, true);
286d0e79aa3SJason Evans }
287d0e79aa3SJason Evans 
288d0e79aa3SJason Evans bool
289b7eaed25SJason Evans pages_decommit(void *addr, size_t size) {
290b7eaed25SJason Evans 	return pages_commit_impl(addr, size, false);
291d0e79aa3SJason Evans }
292d0e79aa3SJason Evans 
293d0e79aa3SJason Evans bool
294b7eaed25SJason Evans pages_purge_lazy(void *addr, size_t size) {
295c5ad8142SEric van Gyzen 	assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);
296b7eaed25SJason Evans 	assert(PAGE_CEILING(size) == size);
297b7eaed25SJason Evans 
298b7eaed25SJason Evans 	if (!pages_can_purge_lazy) {
299b7eaed25SJason Evans 		return true;
300b7eaed25SJason Evans 	}
3010ef50b4eSJason Evans 	if (!pages_can_purge_lazy_runtime) {
3020ef50b4eSJason Evans 		/*
3030ef50b4eSJason Evans 		 * Built with lazy purge enabled, but detected it was not
3040ef50b4eSJason Evans 		 * supported on the current system.
3050ef50b4eSJason Evans 		 */
3060ef50b4eSJason Evans 		return true;
3070ef50b4eSJason Evans 	}
308d0e79aa3SJason Evans 
309d0e79aa3SJason Evans #ifdef _WIN32
310d0e79aa3SJason Evans 	VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE);
311b7eaed25SJason Evans 	return false;
312b7eaed25SJason Evans #elif defined(JEMALLOC_PURGE_MADVISE_FREE)
3130ef50b4eSJason Evans 	return (madvise(addr, size,
3140ef50b4eSJason Evans #  ifdef MADV_FREE
3150ef50b4eSJason Evans 	    MADV_FREE
3160ef50b4eSJason Evans #  else
3170ef50b4eSJason Evans 	    JEMALLOC_MADV_FREE
3180ef50b4eSJason Evans #  endif
3190ef50b4eSJason Evans 	    ) != 0);
320b7eaed25SJason Evans #elif defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \
321b7eaed25SJason Evans     !defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)
322b7eaed25SJason Evans 	return (madvise(addr, size, MADV_DONTNEED) != 0);
323d0e79aa3SJason Evans #else
324b7eaed25SJason Evans 	not_reached();
325d0e79aa3SJason Evans #endif
326d0e79aa3SJason Evans }
327d0e79aa3SJason Evans 
3287fa7f12fSJason Evans bool
329b7eaed25SJason Evans pages_purge_forced(void *addr, size_t size) {
3307fa7f12fSJason Evans 	assert(PAGE_ADDR2BASE(addr) == addr);
3317fa7f12fSJason Evans 	assert(PAGE_CEILING(size) == size);
3327fa7f12fSJason Evans 
333b7eaed25SJason Evans 	if (!pages_can_purge_forced) {
334b7eaed25SJason Evans 		return true;
335b7eaed25SJason Evans 	}
336b7eaed25SJason Evans 
337b7eaed25SJason Evans #if defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \
338b7eaed25SJason Evans     defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)
339b7eaed25SJason Evans 	return (madvise(addr, size, MADV_DONTNEED) != 0);
340b7eaed25SJason Evans #elif defined(JEMALLOC_MAPS_COALESCE)
341b7eaed25SJason Evans 	/* Try to overlay a new demand-zeroed mapping. */
342b7eaed25SJason Evans 	return pages_commit(addr, size);
343b7eaed25SJason Evans #else
344b7eaed25SJason Evans 	not_reached();
345b7eaed25SJason Evans #endif
346b7eaed25SJason Evans }
347b7eaed25SJason Evans 
3480ef50b4eSJason Evans static bool
3490ef50b4eSJason Evans pages_huge_impl(void *addr, size_t size, bool aligned) {
3500ef50b4eSJason Evans 	if (aligned) {
351b7eaed25SJason Evans 		assert(HUGEPAGE_ADDR2BASE(addr) == addr);
352b7eaed25SJason Evans 		assert(HUGEPAGE_CEILING(size) == size);
3530ef50b4eSJason Evans 	}
3540ef50b4eSJason Evans #ifdef JEMALLOC_HAVE_MADVISE_HUGE
3557fa7f12fSJason Evans 	return (madvise(addr, size, MADV_HUGEPAGE) != 0);
3567fa7f12fSJason Evans #else
357b7eaed25SJason Evans 	return true;
3587fa7f12fSJason Evans #endif
3597fa7f12fSJason Evans }
3607fa7f12fSJason Evans 
3617fa7f12fSJason Evans bool
3620ef50b4eSJason Evans pages_huge(void *addr, size_t size) {
3630ef50b4eSJason Evans 	return pages_huge_impl(addr, size, true);
3640ef50b4eSJason Evans }
3650ef50b4eSJason Evans 
3660ef50b4eSJason Evans static bool
3670ef50b4eSJason Evans pages_huge_unaligned(void *addr, size_t size) {
3680ef50b4eSJason Evans 	return pages_huge_impl(addr, size, false);
3690ef50b4eSJason Evans }
3700ef50b4eSJason Evans 
3710ef50b4eSJason Evans static bool
3720ef50b4eSJason Evans pages_nohuge_impl(void *addr, size_t size, bool aligned) {
3730ef50b4eSJason Evans 	if (aligned) {
374b7eaed25SJason Evans 		assert(HUGEPAGE_ADDR2BASE(addr) == addr);
375b7eaed25SJason Evans 		assert(HUGEPAGE_CEILING(size) == size);
3760ef50b4eSJason Evans 	}
3777fa7f12fSJason Evans 
3780ef50b4eSJason Evans #ifdef JEMALLOC_HAVE_MADVISE_HUGE
3797fa7f12fSJason Evans 	return (madvise(addr, size, MADV_NOHUGEPAGE) != 0);
3807fa7f12fSJason Evans #else
381b7eaed25SJason Evans 	return false;
382b7eaed25SJason Evans #endif
383b7eaed25SJason Evans }
384b7eaed25SJason Evans 
3850ef50b4eSJason Evans bool
3860ef50b4eSJason Evans pages_nohuge(void *addr, size_t size) {
3870ef50b4eSJason Evans 	return pages_nohuge_impl(addr, size, true);
3880ef50b4eSJason Evans }
3890ef50b4eSJason Evans 
3900ef50b4eSJason Evans static bool
3910ef50b4eSJason Evans pages_nohuge_unaligned(void *addr, size_t size) {
3920ef50b4eSJason Evans 	return pages_nohuge_impl(addr, size, false);
3930ef50b4eSJason Evans }
3940ef50b4eSJason Evans 
3950ef50b4eSJason Evans bool
3960ef50b4eSJason Evans pages_dontdump(void *addr, size_t size) {
3970ef50b4eSJason Evans 	assert(PAGE_ADDR2BASE(addr) == addr);
3980ef50b4eSJason Evans 	assert(PAGE_CEILING(size) == size);
3990ef50b4eSJason Evans #ifdef JEMALLOC_MADVISE_DONTDUMP
4000ef50b4eSJason Evans 	return madvise(addr, size, MADV_DONTDUMP) != 0;
4010ef50b4eSJason Evans #else
4020ef50b4eSJason Evans 	return false;
4030ef50b4eSJason Evans #endif
4040ef50b4eSJason Evans }
4050ef50b4eSJason Evans 
4060ef50b4eSJason Evans bool
4070ef50b4eSJason Evans pages_dodump(void *addr, size_t size) {
4080ef50b4eSJason Evans 	assert(PAGE_ADDR2BASE(addr) == addr);
4090ef50b4eSJason Evans 	assert(PAGE_CEILING(size) == size);
4100ef50b4eSJason Evans #ifdef JEMALLOC_MADVISE_DONTDUMP
4110ef50b4eSJason Evans 	return madvise(addr, size, MADV_DODUMP) != 0;
4120ef50b4eSJason Evans #else
4130ef50b4eSJason Evans 	return false;
4140ef50b4eSJason Evans #endif
4150ef50b4eSJason Evans }
4160ef50b4eSJason Evans 
4170ef50b4eSJason Evans 
418b7eaed25SJason Evans static size_t
419b7eaed25SJason Evans os_page_detect(void) {
420b7eaed25SJason Evans #ifdef _WIN32
421b7eaed25SJason Evans 	SYSTEM_INFO si;
422b7eaed25SJason Evans 	GetSystemInfo(&si);
423b7eaed25SJason Evans 	return si.dwPageSize;
4240ef50b4eSJason Evans #elif defined(__FreeBSD__)
425c5ad8142SEric van Gyzen 	/*
426c5ad8142SEric van Gyzen 	 * This returns the value obtained from
427c5ad8142SEric van Gyzen 	 * the auxv vector, avoiding a syscall.
428c5ad8142SEric van Gyzen 	 */
4290ef50b4eSJason Evans 	return getpagesize();
430b7eaed25SJason Evans #else
431b7eaed25SJason Evans 	long result = sysconf(_SC_PAGESIZE);
432b7eaed25SJason Evans 	if (result == -1) {
433b7eaed25SJason Evans 		return LG_PAGE;
434b7eaed25SJason Evans 	}
435b7eaed25SJason Evans 	return (size_t)result;
4367fa7f12fSJason Evans #endif
4377fa7f12fSJason Evans }
4387fa7f12fSJason Evans 
4391f0a49e8SJason Evans #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT
4401f0a49e8SJason Evans static bool
441b7eaed25SJason Evans os_overcommits_sysctl(void) {
4421f0a49e8SJason Evans 	int vm_overcommit;
4431f0a49e8SJason Evans 	size_t sz;
444*0ae364adSKonstantin Belousov 	int bsdflags;
445*0ae364adSKonstantin Belousov 
446*0ae364adSKonstantin Belousov 	if (_elf_aux_info(AT_BSDFLAGS, &bsdflags, sizeof(bsdflags)) == 0)
447*0ae364adSKonstantin Belousov 		return ((bsdflags & ELF_BSDF_VMNOOVERCOMMIT) == 0);
448*0ae364adSKonstantin Belousov 
4491f0a49e8SJason Evans 	sz = sizeof(vm_overcommit);
4500ef50b4eSJason Evans #if defined(__FreeBSD__) && defined(VM_OVERCOMMIT)
4510ef50b4eSJason Evans 	int mib[2];
4520ef50b4eSJason Evans 
4530ef50b4eSJason Evans 	mib[0] = CTL_VM;
4540ef50b4eSJason Evans 	mib[1] = VM_OVERCOMMIT;
4550ef50b4eSJason Evans 	if (sysctl(mib, 2, &vm_overcommit, &sz, NULL, 0) != 0) {
4560ef50b4eSJason Evans 		return false; /* Error. */
4570ef50b4eSJason Evans 	}
4580ef50b4eSJason Evans #else
459b7eaed25SJason Evans 	if (sysctlbyname("vm.overcommit", &vm_overcommit, &sz, NULL, 0) != 0) {
460b7eaed25SJason Evans 		return false; /* Error. */
461b7eaed25SJason Evans 	}
4620ef50b4eSJason Evans #endif
4631f0a49e8SJason Evans 
46487384c51SKonstantin Belousov 	return ((vm_overcommit & (SWAP_RESERVE_FORCE_ON |
46587384c51SKonstantin Belousov 	    SWAP_RESERVE_RLIMIT_ON)) == 0);
4661f0a49e8SJason Evans }
4671f0a49e8SJason Evans #endif
4681f0a49e8SJason Evans 
4691f0a49e8SJason Evans #ifdef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY
470bde95144SJason Evans /*
471bde95144SJason Evans  * Use syscall(2) rather than {open,read,close}(2) when possible to avoid
472bde95144SJason Evans  * reentry during bootstrapping if another library has interposed system call
473bde95144SJason Evans  * wrappers.
474bde95144SJason Evans  */
4751f0a49e8SJason Evans static bool
476b7eaed25SJason Evans os_overcommits_proc(void) {
4771f0a49e8SJason Evans 	int fd;
4781f0a49e8SJason Evans 	char buf[1];
4791f0a49e8SJason Evans 
4807fa7f12fSJason Evans #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open)
4810ef50b4eSJason Evans 	#if defined(O_CLOEXEC)
482b7eaed25SJason Evans 		fd = (int)syscall(SYS_open, "/proc/sys/vm/overcommit_memory", O_RDONLY |
483b7eaed25SJason Evans 			O_CLOEXEC);
4840ef50b4eSJason Evans 	#else
4850ef50b4eSJason Evans 		fd = (int)syscall(SYS_open, "/proc/sys/vm/overcommit_memory", O_RDONLY);
4860ef50b4eSJason Evans 		if (fd != -1) {
4870ef50b4eSJason Evans 			fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
4880ef50b4eSJason Evans 		}
4890ef50b4eSJason Evans 	#endif
490b7eaed25SJason Evans #elif defined(JEMALLOC_USE_SYSCALL) && defined(SYS_openat)
4910ef50b4eSJason Evans 	#if defined(O_CLOEXEC)
492b7eaed25SJason Evans 		fd = (int)syscall(SYS_openat,
493b7eaed25SJason Evans 			AT_FDCWD, "/proc/sys/vm/overcommit_memory", O_RDONLY | O_CLOEXEC);
494bde95144SJason Evans 	#else
4950ef50b4eSJason Evans 		fd = (int)syscall(SYS_openat,
4960ef50b4eSJason Evans 			AT_FDCWD, "/proc/sys/vm/overcommit_memory", O_RDONLY);
4970ef50b4eSJason Evans 		if (fd != -1) {
4980ef50b4eSJason Evans 			fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
4990ef50b4eSJason Evans 		}
500bde95144SJason Evans 	#endif
5010ef50b4eSJason Evans #else
5020ef50b4eSJason Evans 	#if defined(O_CLOEXEC)
5030ef50b4eSJason Evans 		fd = open("/proc/sys/vm/overcommit_memory", O_RDONLY | O_CLOEXEC);
5040ef50b4eSJason Evans 	#else
5050ef50b4eSJason Evans 		fd = open("/proc/sys/vm/overcommit_memory", O_RDONLY);
5060ef50b4eSJason Evans 		if (fd != -1) {
5070ef50b4eSJason Evans 			fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
5080ef50b4eSJason Evans 		}
5090ef50b4eSJason Evans 	#endif
5100ef50b4eSJason Evans #endif
5110ef50b4eSJason Evans 
512b7eaed25SJason Evans 	if (fd == -1) {
513b7eaed25SJason Evans 		return false; /* Error. */
514b7eaed25SJason Evans 	}
5151f0a49e8SJason Evans 
5160ef50b4eSJason Evans 	ssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf));
5177fa7f12fSJason Evans #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close)
518bde95144SJason Evans 	syscall(SYS_close, fd);
519bde95144SJason Evans #else
520bde95144SJason Evans 	close(fd);
521bde95144SJason Evans #endif
522bde95144SJason Evans 
523b7eaed25SJason Evans 	if (nread < 1) {
524b7eaed25SJason Evans 		return false; /* Error. */
525b7eaed25SJason Evans 	}
5261f0a49e8SJason Evans 	/*
5271f0a49e8SJason Evans 	 * /proc/sys/vm/overcommit_memory meanings:
5281f0a49e8SJason Evans 	 * 0: Heuristic overcommit.
5291f0a49e8SJason Evans 	 * 1: Always overcommit.
5301f0a49e8SJason Evans 	 * 2: Never overcommit.
5311f0a49e8SJason Evans 	 */
5321f0a49e8SJason Evans 	return (buf[0] == '0' || buf[0] == '1');
5331f0a49e8SJason Evans }
5341f0a49e8SJason Evans #endif
5351f0a49e8SJason Evans 
5360ef50b4eSJason Evans void
5370ef50b4eSJason Evans pages_set_thp_state (void *ptr, size_t size) {
5380ef50b4eSJason Evans 	if (opt_thp == thp_mode_default || opt_thp == init_system_thp_mode) {
5390ef50b4eSJason Evans 		return;
5400ef50b4eSJason Evans 	}
5410ef50b4eSJason Evans 	assert(opt_thp != thp_mode_not_supported &&
5420ef50b4eSJason Evans 	    init_system_thp_mode != thp_mode_not_supported);
5430ef50b4eSJason Evans 
5440ef50b4eSJason Evans 	if (opt_thp == thp_mode_always
5450ef50b4eSJason Evans 	    && init_system_thp_mode != thp_mode_never) {
5460ef50b4eSJason Evans 		assert(init_system_thp_mode == thp_mode_default);
5470ef50b4eSJason Evans 		pages_huge_unaligned(ptr, size);
5480ef50b4eSJason Evans 	} else if (opt_thp == thp_mode_never) {
5490ef50b4eSJason Evans 		assert(init_system_thp_mode == thp_mode_default ||
5500ef50b4eSJason Evans 		    init_system_thp_mode == thp_mode_always);
5510ef50b4eSJason Evans 		pages_nohuge_unaligned(ptr, size);
5520ef50b4eSJason Evans 	}
5530ef50b4eSJason Evans }
5540ef50b4eSJason Evans 
5550ef50b4eSJason Evans static void
5560ef50b4eSJason Evans init_thp_state(void) {
5570ef50b4eSJason Evans 	if (!have_madvise_huge) {
5580ef50b4eSJason Evans 		if (metadata_thp_enabled() && opt_abort) {
5590ef50b4eSJason Evans 			malloc_write("<jemalloc>: no MADV_HUGEPAGE support\n");
5600ef50b4eSJason Evans 			abort();
5610ef50b4eSJason Evans 		}
5620ef50b4eSJason Evans 		goto label_error;
5630ef50b4eSJason Evans 	}
5640ef50b4eSJason Evans 
5650ef50b4eSJason Evans 	static const char sys_state_madvise[] = "always [madvise] never\n";
5660ef50b4eSJason Evans 	static const char sys_state_always[] = "[always] madvise never\n";
5670ef50b4eSJason Evans 	static const char sys_state_never[] = "always madvise [never]\n";
5680ef50b4eSJason Evans 	char buf[sizeof(sys_state_madvise)];
5690ef50b4eSJason Evans 
5700ef50b4eSJason Evans #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open)
5710ef50b4eSJason Evans 	int fd = (int)syscall(SYS_open,
5720ef50b4eSJason Evans 	    "/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY);
5730ef50b4eSJason Evans #else
5740ef50b4eSJason Evans 	int fd = open("/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY);
5750ef50b4eSJason Evans #endif
5760ef50b4eSJason Evans 	if (fd == -1) {
5770ef50b4eSJason Evans 		goto label_error;
5780ef50b4eSJason Evans 	}
5790ef50b4eSJason Evans 
5800ef50b4eSJason Evans 	ssize_t nread = malloc_read_fd(fd, &buf, sizeof(buf));
5810ef50b4eSJason Evans #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_close)
5820ef50b4eSJason Evans 	syscall(SYS_close, fd);
5830ef50b4eSJason Evans #else
5840ef50b4eSJason Evans 	close(fd);
5850ef50b4eSJason Evans #endif
5860ef50b4eSJason Evans 
587c5ad8142SEric van Gyzen         if (nread < 0) {
588c5ad8142SEric van Gyzen 		goto label_error;
589c5ad8142SEric van Gyzen         }
590c5ad8142SEric van Gyzen 
5910ef50b4eSJason Evans 	if (strncmp(buf, sys_state_madvise, (size_t)nread) == 0) {
5920ef50b4eSJason Evans 		init_system_thp_mode = thp_mode_default;
5930ef50b4eSJason Evans 	} else if (strncmp(buf, sys_state_always, (size_t)nread) == 0) {
5940ef50b4eSJason Evans 		init_system_thp_mode = thp_mode_always;
5950ef50b4eSJason Evans 	} else if (strncmp(buf, sys_state_never, (size_t)nread) == 0) {
5960ef50b4eSJason Evans 		init_system_thp_mode = thp_mode_never;
5970ef50b4eSJason Evans 	} else {
5980ef50b4eSJason Evans 		goto label_error;
5990ef50b4eSJason Evans 	}
6000ef50b4eSJason Evans 	return;
6010ef50b4eSJason Evans label_error:
6020ef50b4eSJason Evans 	opt_thp = init_system_thp_mode = thp_mode_not_supported;
6030ef50b4eSJason Evans }
6040ef50b4eSJason Evans 
605b7eaed25SJason Evans bool
606b7eaed25SJason Evans pages_boot(void) {
607b7eaed25SJason Evans 	os_page = os_page_detect();
608b7eaed25SJason Evans 	if (os_page > PAGE) {
609b7eaed25SJason Evans 		malloc_write("<jemalloc>: Unsupported system page size\n");
610b7eaed25SJason Evans 		if (opt_abort) {
611b7eaed25SJason Evans 			abort();
612b7eaed25SJason Evans 		}
613b7eaed25SJason Evans 		return true;
614b7eaed25SJason Evans 	}
6151f0a49e8SJason Evans 
6161f0a49e8SJason Evans #ifndef _WIN32
6171f0a49e8SJason Evans 	mmap_flags = MAP_PRIVATE | MAP_ANON;
6181f0a49e8SJason Evans #endif
6191f0a49e8SJason Evans 
6201f0a49e8SJason Evans #ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT
6211f0a49e8SJason Evans 	os_overcommits = os_overcommits_sysctl();
6221f0a49e8SJason Evans #elif defined(JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY)
6231f0a49e8SJason Evans 	os_overcommits = os_overcommits_proc();
6241f0a49e8SJason Evans #  ifdef MAP_NORESERVE
625b7eaed25SJason Evans 	if (os_overcommits) {
6261f0a49e8SJason Evans 		mmap_flags |= MAP_NORESERVE;
627b7eaed25SJason Evans 	}
6281f0a49e8SJason Evans #  endif
6291f0a49e8SJason Evans #else
6301f0a49e8SJason Evans 	os_overcommits = false;
6311f0a49e8SJason Evans #endif
632b7eaed25SJason Evans 
6330ef50b4eSJason Evans 	init_thp_state();
6340ef50b4eSJason Evans 
635bff19560SEdward Tomasz Napierala #ifdef __FreeBSD__
636bff19560SEdward Tomasz Napierala 	/*
637bff19560SEdward Tomasz Napierala 	 * FreeBSD doesn't need the check; madvise(2) is known to work.
638bff19560SEdward Tomasz Napierala 	 */
639bff19560SEdward Tomasz Napierala #else
6400ef50b4eSJason Evans 	/* Detect lazy purge runtime support. */
6410ef50b4eSJason Evans 	if (pages_can_purge_lazy) {
6420ef50b4eSJason Evans 		bool committed = false;
6430ef50b4eSJason Evans 		void *madv_free_page = os_pages_map(NULL, PAGE, PAGE, &committed);
6440ef50b4eSJason Evans 		if (madv_free_page == NULL) {
6450ef50b4eSJason Evans 			return true;
6460ef50b4eSJason Evans 		}
6470ef50b4eSJason Evans 		assert(pages_can_purge_lazy_runtime);
6480ef50b4eSJason Evans 		if (pages_purge_lazy(madv_free_page, PAGE)) {
6490ef50b4eSJason Evans 			pages_can_purge_lazy_runtime = false;
6500ef50b4eSJason Evans 		}
6510ef50b4eSJason Evans 		os_pages_unmap(madv_free_page, PAGE);
6520ef50b4eSJason Evans 	}
653bff19560SEdward Tomasz Napierala #endif
6540ef50b4eSJason Evans 
655b7eaed25SJason Evans 	return false;
6561f0a49e8SJason Evans }
657