blob: 4ab9f3df9eacf843075600be540ca6e9ce6bd9f5 [file] [log] [blame]
Jason Evans4201af02010-01-24 02:53:40 -08001#define JEMALLOC_CHUNK_MMAP_C_
Jason Evans376b1522010-02-11 14:45:59 -08002#include "jemalloc/internal/jemalloc_internal.h"
Jason Evans4201af02010-01-24 02:53:40 -08003
4/******************************************************************************/
5/* Data. */
6
7/*
8 * Used by chunk_alloc_mmap() to decide whether to attempt the fast path and
9 * potentially avoid some system calls. We can get away without TLS here,
10 * since the state of mmap_unaligned only affects performance, rather than
11 * correct function.
12 */
13static
14#ifndef NO_TLS
15 __thread
16#endif
17 bool mmap_unaligned
18#ifndef NO_TLS
19 JEMALLOC_ATTR(tls_model("initial-exec"))
20#endif
21 ;
22
23/******************************************************************************/
24/* Function prototypes for non-inline static functions. */
25
26static void *pages_map(void *addr, size_t size);
27static void pages_unmap(void *addr, size_t size);
28static void *chunk_alloc_mmap_slow(size_t size, bool unaligned);
29
30/******************************************************************************/
31
32static void *
33pages_map(void *addr, size_t size)
34{
35 void *ret;
36
37 /*
38 * We don't use MAP_FIXED here, because it can cause the *replacement*
39 * of existing mappings, and we only want to create new mappings.
40 */
41 ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
42 -1, 0);
43 assert(ret != NULL);
44
45 if (ret == MAP_FAILED)
46 ret = NULL;
47 else if (addr != NULL && ret != addr) {
48 /*
49 * We succeeded in mapping memory, but not in the right place.
50 */
51 if (munmap(ret, size) == -1) {
52 char buf[STRERROR_BUF];
53
54 strerror_r(errno, buf, sizeof(buf));
55 malloc_write4("<jemalloc>", ": Error in munmap(): ",
56 buf, "\n");
57 if (opt_abort)
58 abort();
59 }
60 ret = NULL;
61 }
62
63 assert(ret == NULL || (addr == NULL && ret != addr)
64 || (addr != NULL && ret == addr));
65 return (ret);
66}
67
68static void
69pages_unmap(void *addr, size_t size)
70{
71
72 if (munmap(addr, size) == -1) {
73 char buf[STRERROR_BUF];
74
75 strerror_r(errno, buf, sizeof(buf));
76 malloc_write4("<jemalloc>", ": Error in munmap(): ", buf, "\n");
77 if (opt_abort)
78 abort();
79 }
80}
81
82static void *
83chunk_alloc_mmap_slow(size_t size, bool unaligned)
84{
85 void *ret;
86 size_t offset;
87
88 /* Beware size_t wrap-around. */
89 if (size + chunksize <= size)
90 return (NULL);
91
92 ret = pages_map(NULL, size + chunksize);
93 if (ret == NULL)
94 return (NULL);
95
96 /* Clean up unneeded leading/trailing space. */
97 offset = CHUNK_ADDR2OFFSET(ret);
98 if (offset != 0) {
99 /* Note that mmap() returned an unaligned mapping. */
100 unaligned = true;
101
102 /* Leading space. */
103 pages_unmap(ret, chunksize - offset);
104
105 ret = (void *)((uintptr_t)ret +
106 (chunksize - offset));
107
108 /* Trailing space. */
109 pages_unmap((void *)((uintptr_t)ret + size),
110 offset);
111 } else {
112 /* Trailing space only. */
113 pages_unmap((void *)((uintptr_t)ret + size),
114 chunksize);
115 }
116
117 /*
118 * If mmap() returned an aligned mapping, reset mmap_unaligned so that
119 * the next chunk_alloc_mmap() execution tries the fast allocation
120 * method.
121 */
122 if (unaligned == false)
123 mmap_unaligned = false;
124
125 return (ret);
126}
127
128void *
129chunk_alloc_mmap(size_t size)
130{
131 void *ret;
132
133 /*
134 * Ideally, there would be a way to specify alignment to mmap() (like
135 * NetBSD has), but in the absence of such a feature, we have to work
136 * hard to efficiently create aligned mappings. The reliable, but
137 * slow method is to create a mapping that is over-sized, then trim the
138 * excess. However, that always results in at least one call to
139 * pages_unmap().
140 *
141 * A more optimistic approach is to try mapping precisely the right
142 * amount, then try to append another mapping if alignment is off. In
143 * practice, this works out well as long as the application is not
144 * interleaving mappings via direct mmap() calls. If we do run into a
145 * situation where there is an interleaved mapping and we are unable to
146 * extend an unaligned mapping, our best option is to switch to the
147 * slow method until mmap() returns another aligned mapping. This will
148 * tend to leave a gap in the memory map that is too small to cause
149 * later problems for the optimistic method.
150 *
151 * Another possible confounding factor is address space layout
152 * randomization (ASLR), which causes mmap(2) to disregard the
153 * requested address. mmap_unaligned tracks whether the previous
154 * chunk_alloc_mmap() execution received any unaligned or relocated
155 * mappings, and if so, the current execution will immediately fall
156 * back to the slow method. However, we keep track of whether the fast
157 * method would have succeeded, and if so, we make a note to try the
158 * fast method next time.
159 */
160
161 if (mmap_unaligned == false) {
162 size_t offset;
163
164 ret = pages_map(NULL, size);
165 if (ret == NULL)
166 return (NULL);
167
168 offset = CHUNK_ADDR2OFFSET(ret);
169 if (offset != 0) {
170 mmap_unaligned = true;
171 /* Try to extend chunk boundary. */
172 if (pages_map((void *)((uintptr_t)ret + size),
173 chunksize - offset) == NULL) {
174 /*
175 * Extension failed. Clean up, then revert to
176 * the reliable-but-expensive method.
177 */
178 pages_unmap(ret, size);
179 ret = chunk_alloc_mmap_slow(size, true);
180 } else {
181 /* Clean up unneeded leading space. */
182 pages_unmap(ret, chunksize - offset);
183 ret = (void *)((uintptr_t)ret + (chunksize -
184 offset));
185 }
186 }
Jason Evans4fb7f512010-01-27 18:27:09 -0800187 } else
Jason Evans4201af02010-01-24 02:53:40 -0800188 ret = chunk_alloc_mmap_slow(size, false);
189
190 return (ret);
191}
192
193void
194chunk_dealloc_mmap(void *chunk, size_t size)
195{
196
197 pages_unmap(chunk, size);
198}