xref: /oneTBB/src/tbbmalloc/MapMemory.h (revision cd6a5f9f)
1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #ifndef _itt_shared_malloc_MapMemory_H
18 #define _itt_shared_malloc_MapMemory_H
19 
20 #include <stdlib.h>
21 
22 #if __unix__ || __APPLE__ || __sun || __FreeBSD__
23 
24 #if __sun && !defined(_XPG4_2)
25  // To have void* as mmap's 1st argument
26  #define _XPG4_2 1
27  #define XPG4_WAS_DEFINED 1
28 #endif
29 
30 #include <sys/mman.h>
31 #if __unix__
32 /* __TBB_MAP_HUGETLB is MAP_HUGETLB from system header linux/mman.h.
33    The header is not included here, as on some Linux flavors inclusion of
34    linux/mman.h leads to compilation error,
35    while changing of MAP_HUGETLB is highly unexpected.
36 */
37 #define __TBB_MAP_HUGETLB 0x40000
38 #else
39 #define __TBB_MAP_HUGETLB 0
40 #endif
41 
42 #if XPG4_WAS_DEFINED
43  #undef _XPG4_2
44  #undef XPG4_WAS_DEFINED
45 #endif
46 
47 inline void* mmap_impl(size_t map_size, void* map_hint = nullptr, int map_flags = 0) {
48 #ifndef MAP_ANONYMOUS
49 // macOS* defines MAP_ANON, which is deprecated in Linux*.
50 #define MAP_ANONYMOUS MAP_ANON
51 #endif /* MAP_ANONYMOUS */
52     return mmap(map_hint, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | map_flags, -1, 0);
53 }
54 
55 inline void* mmapTHP(size_t bytes) {
56     // Initializes in zero-initialized data section
57     static void* hint;
58 
59     // Optimistically try to use a last huge page aligned region end
60     // as a hint for mmap.
61     hint = hint ? (void*)((uintptr_t)hint - bytes) : hint;
62     void* result = mmap_impl(bytes, hint);
63 
64     // Something went wrong
65     if (result == MAP_FAILED) {
66         hint = nullptr;
67         return MAP_FAILED;
68     }
69 
70     // Otherwise, fall back to the slow path - map oversized region
71     // and trim excess parts.
72     if (!isAligned(result, HUGE_PAGE_SIZE)) {
73         // Undo previous try
74         munmap(result, bytes);
75 
76         // Map oversized on huge page size region
77         result = mmap_impl(bytes + HUGE_PAGE_SIZE);
78 
79         // Something went wrong
80         if (result == MAP_FAILED) {
81             hint = nullptr;
82             return MAP_FAILED;
83         }
84 
85         // Misalignment offset
86         uintptr_t offset = 0;
87 
88         if (!isAligned(result, HUGE_PAGE_SIZE)) {
89             // Trim excess head of a region if it is no aligned
90             offset = HUGE_PAGE_SIZE - ((uintptr_t)result & (HUGE_PAGE_SIZE - 1));
91             munmap(result, offset);
92 
93             // New region beginning
94             result = (void*)((uintptr_t)result + offset);
95         }
96 
97         // Trim excess tail of a region
98         munmap((void*)((uintptr_t)result + bytes), HUGE_PAGE_SIZE - offset);
99     }
100 
101     // Assume, that mmap virtual addresses grow down by default
102     // So, set a hint as a result of a last successful allocation
103     // and then use it minus requested size as a new mapping point.
104     // TODO: Atomic store is meant here, fence not needed, but
105     // currently we don't have such function.
106     hint = result;
107 
108     MALLOC_ASSERT(isAligned(result, HUGE_PAGE_SIZE), "Mapped address is not aligned on huge page size.");
109 
110     return result;
111 }
112 
113 #define MEMORY_MAPPING_USES_MALLOC 0
114 void* MapMemory (size_t bytes, PageType pageType)
115 {
116     void* result = nullptr;
117     int prevErrno = errno;
118 
119     switch (pageType) {
120         case REGULAR:
121         {
122             result = mmap_impl(bytes);
123             break;
124         }
125         case PREALLOCATED_HUGE_PAGE:
126         {
127             MALLOC_ASSERT((bytes % HUGE_PAGE_SIZE) == 0, "Mapping size should be divisible by huge page size");
128             result = mmap_impl(bytes, nullptr, __TBB_MAP_HUGETLB);
129             break;
130         }
131         case TRANSPARENT_HUGE_PAGE:
132         {
133             MALLOC_ASSERT((bytes % HUGE_PAGE_SIZE) == 0, "Mapping size should be divisible by huge page size");
134             result = mmapTHP(bytes);
135             break;
136         }
137         default:
138         {
139             MALLOC_ASSERT(false, "Unknown page type");
140         }
141     }
142 
143     if (result == MAP_FAILED) {
144         errno = prevErrno;
145         return nullptr;
146     }
147 
148     return result;
149 }
150 
151 int UnmapMemory(void *area, size_t bytes)
152 {
153     int prevErrno = errno;
154     int ret = munmap(area, bytes);
155     if (-1 == ret)
156         errno = prevErrno;
157     return ret;
158 }
159 
160 #elif (_WIN32 || _WIN64) && !__TBB_WIN8UI_SUPPORT
161 #include <windows.h>
162 
163 #define MEMORY_MAPPING_USES_MALLOC 0
164 void* MapMemory (size_t bytes, PageType)
165 {
166     /* Is VirtualAlloc thread safe? */
167     return VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
168 }
169 
170 int UnmapMemory(void *area, size_t /*bytes*/)
171 {
172     BOOL result = VirtualFree(area, 0, MEM_RELEASE);
173     return !result;
174 }
175 
176 #else
177 
178 void *ErrnoPreservingMalloc(size_t bytes)
179 {
180     int prevErrno = errno;
181     void *ret = malloc( bytes );
182     if (!ret)
183         errno = prevErrno;
184     return ret;
185 }
186 
187 #define MEMORY_MAPPING_USES_MALLOC 1
188 void* MapMemory (size_t bytes, PageType)
189 {
190     return ErrnoPreservingMalloc( bytes );
191 }
192 
193 int UnmapMemory(void *area, size_t /*bytes*/)
194 {
195     free( area );
196     return 0;
197 }
198 
199 #endif /* OS dependent */
200 
201 #if MALLOC_CHECK_RECURSION && MEMORY_MAPPING_USES_MALLOC
202 #error Impossible to protect against malloc recursion when memory mapping uses malloc.
203 #endif
204 
205 #endif /* _itt_shared_malloc_MapMemory_H */
206