bmalloc
[WebKit-https.git] / Source / bmalloc / bmalloc / VMAllocate.h
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #ifndef VMAllocate_h
27 #define VMAllocate_h
28
29 #include "BAssert.h"
30 #include "Range.h"
31 #include "Sizes.h"
32 #include "Syscall.h"
33 #include <algorithm>
34 #include <mach/vm_statistics.h>
35 #include <sys/mman.h>
36 #include <unistd.h>
37
38 namespace bmalloc {
39
40 #define BMALLOC_VM_TAG VM_MAKE_TAG(VM_MEMORY_TCMALLOC)
41     
42 static const size_t vmPageSize = 16 * kB; // Least upper bound of the OS's we support.
43 static const size_t vmPageMask = ~(vmPageSize - 1);
44     
45 inline size_t vmSize(size_t size)
46 {
47     return roundUpToMultipleOf<vmPageSize>(size);
48 }
49     
50 inline void vmValidate(size_t vmSize)
51 {
52     ASSERT(vmSize);
53     ASSERT(vmSize == bmalloc::vmSize(vmSize));
54 }
55
56 inline void vmValidate(void* p, size_t vmSize)
57 {
58     vmValidate(vmSize);
59     
60     // We use getpagesize() here instead of vmPageSize because vmPageSize is
61     // allowed to be larger than the OS's true page size.
62     ASSERT(p);
63     ASSERT(p == mask(p, ~(getpagesize() - 1)));
64 }
65
66 inline void* vmAllocate(size_t vmSize)
67 {
68     vmValidate(vmSize);
69     return mmap(0, vmSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, BMALLOC_VM_TAG, 0);
70 }
71
72 inline void vmDeallocate(void* p, size_t vmSize)
73 {
74     vmValidate(p, vmSize);
75     munmap(p, vmSize);
76 }
77
78 // Allocates vmSize bytes at a specified offset from a power-of-two alignment.
79 // Use this function to create pointer masks that aren't simple powers of two.
80
81 inline std::pair<void*, Range> vmAllocate(size_t vmSize, size_t alignment, size_t offset)
82 {
83     vmValidate(vmSize);
84     ASSERT(isPowerOfTwo(alignment));
85
86     size_t mappedSize = std::max(vmSize, alignment) + alignment;
87     char* mapped = static_cast<char*>(vmAllocate(mappedSize));
88     
89     uintptr_t alignmentMask = alignment - 1;
90     if (!test(mapped, alignmentMask) && offset + vmSize <= alignment) {
91         // We got two perfectly aligned regions. Give one back to avoid wasting
92         // VM unnecessarily. This isn't costly because we aren't making holes.
93         vmDeallocate(mapped + alignment, alignment);
94         return std::make_pair(mapped + offset, Range(mapped, alignment));
95     }
96
97     // We got an unaligned region. Keep the whole thing to avoid creating holes,
98     // and hopefully realign the VM allocator for future allocations. On Darwin,
99     // VM holes trigger O(N^2) behavior in mmap, so we want to minimize them.
100     char* mappedAligned = mask(mapped, ~alignmentMask) + alignment;
101     return std::make_pair(mappedAligned + offset, Range(mapped, mappedSize));
102 }
103
104 inline void vmDeallocatePhysicalPages(void* p, size_t vmSize)
105 {
106     vmValidate(p, vmSize);
107     SYSCALL(madvise(p, vmSize, MADV_FREE_REUSABLE));
108 }
109
110 inline void vmAllocatePhysicalPages(void* p, size_t vmSize)
111 {
112     vmValidate(p, vmSize);
113     SYSCALL(madvise(p, vmSize, MADV_FREE_REUSE));
114 }
115
116 // Trims requests that are un-page-aligned. NOTE: size must be at least a page.
117 inline void vmDeallocatePhysicalPagesSloppy(void* p, size_t size)
118 {
119     ASSERT(size >= vmPageSize);
120
121     char* begin = roundUpToMultipleOf<vmPageSize>(static_cast<char*>(p));
122     char* end = roundDownToMultipleOf<vmPageSize>(static_cast<char*>(p) + size);
123
124     Range range(begin, end - begin);
125     if (!range)
126         return;
127     vmDeallocatePhysicalPages(range.begin(), range.size());
128 }
129
130 // Expands requests that are un-page-aligned. NOTE: Allocation must proceed left-to-right.
131 inline void vmAllocatePhysicalPagesSloppy(void* p, size_t size)
132 {
133     char* begin = roundUpToMultipleOf<vmPageSize>(static_cast<char*>(p));
134     char* end = roundUpToMultipleOf<vmPageSize>(static_cast<char*>(p) + size);
135
136     Range range(begin, end - begin);
137     if (!range)
138         return;
139     vmAllocatePhysicalPages(range.begin(), range.size());
140 }
141
142 } // namespace bmalloc
143
144 #endif // VMAllocate_h