1 // Copyright (c) 2005, 2007, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 // Author: Sanjay Ghemawat
34 #include "TCSystemAlloc.h"
38 #include "Assertions.h"
39 #include "TCSpinLock.h"
40 #include "UnusedParam.h"
45 #elif HAVE(INTTYPES_H)
48 #include <sys/types.h>
60 #define MAP_ANONYMOUS MAP_ANON
65 // Structure for discovering alignment
72 static SpinLock spinlock
= SPINLOCK_INITIALIZER
;
74 // Page size is initialized on demand
75 static size_t pagesize
= 0;
77 // Configuration parameters.
79 // if use_devmem is true, either use_sbrk or use_mmap must also be true.
80 // For 2.2 kernels, it looks like the sbrk address space (500MBish) and
81 // the mmap address space (1300MBish) are disjoint, so we need both allocators
82 // to get as much virtual memory as possible.
84 static bool use_devmem
= false;
88 static bool use_sbrk
= false;
92 static bool use_mmap
= true;
95 #if HAVE(VIRTUALALLOC)
96 static bool use_VirtualAlloc
= true;
99 // Flags to keep us from retrying allocators that failed.
100 static bool devmem_failure
= false;
101 static bool sbrk_failure
= false;
102 static bool mmap_failure
= false;
103 static bool VirtualAlloc_failure
= false;
106 DEFINE_int32(malloc_devmem_start
, 0,
107 "Physical memory starting location in MB for /dev/mem allocation."
108 " Setting this to 0 disables /dev/mem allocation");
109 DEFINE_int32(malloc_devmem_limit
, 0,
110 "Physical memory limit location in MB for /dev/mem allocation."
111 " Setting this to 0 means no limit.");
113 static const int32_t FLAGS_malloc_devmem_start
= 0;
114 static const int32_t FLAGS_malloc_devmem_limit
= 0;
119 static void* TrySbrk(size_t size
, size_t *actual_size
, size_t alignment
) {
120 size
= ((size
+ alignment
- 1) / alignment
) * alignment
;
122 // could theoretically return the "extra" bytes here, but this
123 // is simple and correct.
127 void* result
= sbrk(size
);
128 if (result
== reinterpret_cast<void*>(-1)) {
134 uintptr_t ptr
= reinterpret_cast<uintptr_t>(result
);
135 if ((ptr
& (alignment
-1)) == 0) return result
;
137 // Try to get more memory for alignment
138 size_t extra
= alignment
- (ptr
& (alignment
-1));
139 void* r2
= sbrk(extra
);
140 if (reinterpret_cast<uintptr_t>(r2
) == (ptr
+ size
)) {
141 // Contiguous with previous result
142 return reinterpret_cast<void*>(ptr
+ extra
);
145 // Give up and ask for "size + alignment - 1" bytes so
146 // that we can find an aligned region within it.
147 result
= sbrk(size
+ alignment
- 1);
148 if (result
== reinterpret_cast<void*>(-1)) {
152 ptr
= reinterpret_cast<uintptr_t>(result
);
153 if ((ptr
& (alignment
-1)) != 0) {
154 ptr
+= alignment
- (ptr
& (alignment
-1));
156 return reinterpret_cast<void*>(ptr
);
159 #endif /* HAVE(SBRK) */
163 static void* TryMmap(size_t size
, size_t *actual_size
, size_t alignment
) {
164 // Enforce page alignment
165 if (pagesize
== 0) pagesize
= getpagesize();
166 if (alignment
< pagesize
) alignment
= pagesize
;
167 size
= ((size
+ alignment
- 1) / alignment
) * alignment
;
169 // could theoretically return the "extra" bytes here, but this
170 // is simple and correct.
174 // Ask for extra memory if alignment > pagesize
176 if (alignment
> pagesize
) {
177 extra
= alignment
- pagesize
;
179 void* result
= mmap(NULL
, size
+ extra
,
180 PROT_READ
| PROT_WRITE
,
181 MAP_PRIVATE
|MAP_ANONYMOUS
,
182 VM_TAG_FOR_TCMALLOC_MEMORY
, 0);
183 if (result
== reinterpret_cast<void*>(MAP_FAILED
)) {
188 // Adjust the return memory so it is aligned
189 uintptr_t ptr
= reinterpret_cast<uintptr_t>(result
);
191 if ((ptr
& (alignment
- 1)) != 0) {
192 adjust
= alignment
- (ptr
& (alignment
- 1));
195 // Return the unused memory to the system
197 munmap(reinterpret_cast<void*>(ptr
), adjust
);
199 if (adjust
< extra
) {
200 munmap(reinterpret_cast<void*>(ptr
+ adjust
+ size
), extra
- adjust
);
204 return reinterpret_cast<void*>(ptr
);
207 #endif /* HAVE(MMAP) */
209 #if HAVE(VIRTUALALLOC)
211 static void* TryVirtualAlloc(size_t size
, size_t *actual_size
, size_t alignment
) {
212 // Enforce page alignment
214 SYSTEM_INFO system_info
;
215 GetSystemInfo(&system_info
);
216 pagesize
= system_info
.dwPageSize
;
219 if (alignment
< pagesize
) alignment
= pagesize
;
220 size
= ((size
+ alignment
- 1) / alignment
) * alignment
;
222 // could theoretically return the "extra" bytes here, but this
223 // is simple and correct.
227 // Ask for extra memory if alignment > pagesize
229 if (alignment
> pagesize
) {
230 extra
= alignment
- pagesize
;
232 void* result
= VirtualAlloc(NULL
, size
+ extra
,
233 MEM_RESERVE
| MEM_COMMIT
| MEM_TOP_DOWN
,
236 if (result
== NULL
) {
237 VirtualAlloc_failure
= true;
241 // Adjust the return memory so it is aligned
242 uintptr_t ptr
= reinterpret_cast<uintptr_t>(result
);
244 if ((ptr
& (alignment
- 1)) != 0) {
245 adjust
= alignment
- (ptr
& (alignment
- 1));
248 // Return the unused memory to the system - we'd like to release but the best we can do
249 // is decommit, since Windows only lets you free the whole allocation.
251 VirtualFree(reinterpret_cast<void*>(ptr
), adjust
, MEM_DECOMMIT
);
253 if (adjust
< extra
) {
254 VirtualFree(reinterpret_cast<void*>(ptr
+ adjust
+ size
), extra
-adjust
, MEM_DECOMMIT
);
258 return reinterpret_cast<void*>(ptr
);
261 #endif /* HAVE(MMAP) */
264 static void* TryDevMem(size_t size
, size_t *actual_size
, size_t alignment
) {
265 static bool initialized
= false;
266 static off_t physmem_base
; // next physical memory address to allocate
267 static off_t physmem_limit
; // maximum physical address allowed
268 static int physmem_fd
; // file descriptor for /dev/mem
270 // Check if we should use /dev/mem allocation. Note that it may take
271 // a while to get this flag initialized, so meanwhile we fall back to
272 // the next allocator. (It looks like 7MB gets allocated before
273 // this flag gets initialized -khr.)
274 if (FLAGS_malloc_devmem_start
== 0) {
275 // NOTE: not a devmem_failure - we'd like TCMalloc_SystemAlloc to
276 // try us again next time.
281 physmem_fd
= open("/dev/mem", O_RDWR
);
282 if (physmem_fd
< 0) {
283 devmem_failure
= true;
286 physmem_base
= FLAGS_malloc_devmem_start
*1024LL*1024LL;
287 physmem_limit
= FLAGS_malloc_devmem_limit
*1024LL*1024LL;
291 // Enforce page alignment
292 if (pagesize
== 0) pagesize
= getpagesize();
293 if (alignment
< pagesize
) alignment
= pagesize
;
294 size
= ((size
+ alignment
- 1) / alignment
) * alignment
;
296 // could theoretically return the "extra" bytes here, but this
297 // is simple and correct.
301 // Ask for extra memory if alignment > pagesize
303 if (alignment
> pagesize
) {
304 extra
= alignment
- pagesize
;
307 // check to see if we have any memory left
308 if (physmem_limit
!= 0 && physmem_base
+ size
+ extra
> physmem_limit
) {
309 devmem_failure
= true;
312 void *result
= mmap(0, size
+ extra
, PROT_READ
| PROT_WRITE
,
313 MAP_SHARED
, physmem_fd
, physmem_base
);
314 if (result
== reinterpret_cast<void*>(MAP_FAILED
)) {
315 devmem_failure
= true;
318 uintptr_t ptr
= reinterpret_cast<uintptr_t>(result
);
320 // Adjust the return memory so it is aligned
322 if ((ptr
& (alignment
- 1)) != 0) {
323 adjust
= alignment
- (ptr
& (alignment
- 1));
326 // Return the unused virtual memory to the system
328 munmap(reinterpret_cast<void*>(ptr
), adjust
);
330 if (adjust
< extra
) {
331 munmap(reinterpret_cast<void*>(ptr
+ adjust
+ size
), extra
- adjust
);
335 physmem_base
+= adjust
+ size
;
337 return reinterpret_cast<void*>(ptr
);
341 void* TCMalloc_SystemAlloc(size_t size
, size_t *actual_size
, size_t alignment
) {
342 // Discard requests that overflow
343 if (size
+ alignment
< size
) return NULL
;
345 SpinLockHolder
lock_holder(&spinlock
);
347 // Enforce minimum alignment
348 if (alignment
< sizeof(MemoryAligner
)) alignment
= sizeof(MemoryAligner
);
350 // Try twice, once avoiding allocators that failed before, and once
351 // more trying all allocators even if they failed before.
352 for (int i
= 0; i
< 2; i
++) {
355 if (use_devmem
&& !devmem_failure
) {
356 void* result
= TryDevMem(size
, actual_size
, alignment
);
357 if (result
!= NULL
) return result
;
362 if (use_sbrk
&& !sbrk_failure
) {
363 void* result
= TrySbrk(size
, actual_size
, alignment
);
364 if (result
!= NULL
) return result
;
369 if (use_mmap
&& !mmap_failure
) {
370 void* result
= TryMmap(size
, actual_size
, alignment
);
371 if (result
!= NULL
) return result
;
375 #if HAVE(VIRTUALALLOC)
376 if (use_VirtualAlloc
&& !VirtualAlloc_failure
) {
377 void* result
= TryVirtualAlloc(size
, actual_size
, alignment
);
378 if (result
!= NULL
) return result
;
382 // nothing worked - reset failure flags and try again
383 devmem_failure
= false;
384 sbrk_failure
= false;
385 mmap_failure
= false;
386 VirtualAlloc_failure
= false;
391 #if HAVE(MADV_FREE_REUSE)
393 void TCMalloc_SystemRelease(void* start
, size_t length
)
395 while (madvise(start
, length
, MADV_FREE_REUSABLE
) == -1 && errno
== EAGAIN
) { }
398 #elif HAVE(MADV_FREE) || HAVE(MADV_DONTNEED)
400 void TCMalloc_SystemRelease(void* start
, size_t length
)
402 // MADV_FREE clears the modified bit on pages, which allows
403 // them to be discarded immediately.
405 const int advice
= MADV_FREE
;
407 const int advice
= MADV_DONTNEED
;
409 if (FLAGS_malloc_devmem_start
) {
410 // It's not safe to use MADV_DONTNEED if we've been mapping
411 // /dev/mem for heap memory
414 if (pagesize
== 0) pagesize
= getpagesize();
415 const size_t pagemask
= pagesize
- 1;
417 size_t new_start
= reinterpret_cast<size_t>(start
);
418 size_t end
= new_start
+ length
;
419 size_t new_end
= end
;
421 // Round up the starting address and round down the ending address
422 // to be page aligned:
423 new_start
= (new_start
+ pagesize
- 1) & ~pagemask
;
424 new_end
= new_end
& ~pagemask
;
426 ASSERT((new_start
& pagemask
) == 0);
427 ASSERT((new_end
& pagemask
) == 0);
428 ASSERT(new_start
>= reinterpret_cast<size_t>(start
));
429 ASSERT(new_end
<= end
);
431 if (new_end
> new_start
) {
432 // Note -- ignoring most return codes, because if this fails it
434 while (madvise(reinterpret_cast<char*>(new_start
), new_end
- new_start
,
444 void TCMalloc_SystemRelease(void* start
, size_t length
)
446 void* newAddress
= mmap(start
, length
, PROT_READ
| PROT_WRITE
, MAP_PRIVATE
| MAP_ANONYMOUS
| MAP_FIXED
, -1, 0);
447 // If the mmap failed then that's ok, we just won't return the memory to the system.
448 ASSERT_UNUSED(newAddress
, newAddress
== start
|| newAddress
== reinterpret_cast<void*>(MAP_FAILED
));
451 #elif HAVE(VIRTUALALLOC)
453 void TCMalloc_SystemRelease(void* start
, size_t length
)
455 if (VirtualFree(start
, length
, MEM_DECOMMIT
))
458 // The decommit may fail if the memory region consists of allocations
459 // from more than one call to VirtualAlloc. In this case, fall back to
460 // using VirtualQuery to retrieve the allocation boundaries and decommit
461 // them each individually.
463 char* ptr
= static_cast<char*>(start
);
464 char* end
= ptr
+ length
;
465 MEMORY_BASIC_INFORMATION info
;
467 size_t resultSize
= VirtualQuery(ptr
, &info
, sizeof(info
));
468 ASSERT_UNUSED(resultSize
, resultSize
== sizeof(info
));
470 size_t decommitSize
= min
<size_t>(info
.RegionSize
, end
- ptr
);
471 BOOL success
= VirtualFree(ptr
, decommitSize
, MEM_DECOMMIT
);
472 ASSERT_UNUSED(success
, success
);
479 // Platforms that don't support returning memory use an empty inline version of TCMalloc_SystemRelease
480 // declared in TCSystemAlloc.h
484 #if HAVE(MADV_FREE_REUSE)
486 void TCMalloc_SystemCommit(void* start
, size_t length
)
488 while (madvise(start
, length
, MADV_FREE_REUSE
) == -1 && errno
== EAGAIN
) { }
491 #elif HAVE(VIRTUALALLOC)
493 void TCMalloc_SystemCommit(void* start
, size_t length
)
495 if (VirtualAlloc(start
, length
, MEM_COMMIT
, PAGE_READWRITE
) == start
)
498 // The commit may fail if the memory region consists of allocations
499 // from more than one call to VirtualAlloc. In this case, fall back to
500 // using VirtualQuery to retrieve the allocation boundaries and commit them
501 // each individually.
503 char* ptr
= static_cast<char*>(start
);
504 char* end
= ptr
+ length
;
505 MEMORY_BASIC_INFORMATION info
;
507 size_t resultSize
= VirtualQuery(ptr
, &info
, sizeof(info
));
508 ASSERT_UNUSED(resultSize
, resultSize
== sizeof(info
));
510 size_t commitSize
= min
<size_t>(info
.RegionSize
, end
- ptr
);
511 void* newAddress
= VirtualAlloc(ptr
, commitSize
, MEM_COMMIT
, PAGE_READWRITE
);
512 ASSERT_UNUSED(newAddress
, newAddress
== ptr
);
519 // Platforms that don't need to explicitly commit memory use an empty inline version of TCMalloc_SystemCommit
520 // declared in TCSystemAlloc.h