| 1 | // Copyright 2009-2021 Intel Corporation | 
| 2 | // SPDX-License-Identifier: Apache-2.0 | 
| 3 |  | 
| 4 | #include "alloc.h" | 
| 5 | #include "intrinsics.h" | 
| 6 | #include "sysinfo.h" | 
| 7 | #include "mutex.h" | 
| 8 |  | 
| 9 | //////////////////////////////////////////////////////////////////////////////// | 
| 10 | /// All Platforms | 
| 11 | //////////////////////////////////////////////////////////////////////////////// | 
| 12 |    | 
| 13 | namespace embree | 
| 14 | { | 
| 15 |   void* alignedMalloc(size_t size, size_t align)  | 
| 16 |   { | 
| 17 |     if (size == 0) | 
| 18 |       return nullptr; | 
| 19 |      | 
| 20 |     assert((align & (align-1)) == 0); | 
| 21 |     void* ptr = _mm_malloc(size: size,align: align); | 
| 22 |  | 
| 23 |     if (size != 0 && ptr == nullptr) | 
| 24 |       throw std::bad_alloc(); | 
| 25 |      | 
| 26 |     return ptr; | 
| 27 |   } | 
| 28 |    | 
| 29 |   void alignedFree(void* ptr) | 
| 30 |   { | 
| 31 |     if (ptr) | 
| 32 |       _mm_free(p: ptr); | 
| 33 |   } | 
| 34 |  | 
| 35 |   static bool huge_pages_enabled = false; | 
| 36 |   static MutexSys os_init_mutex; | 
| 37 |  | 
| 38 |   __forceinline bool isHugePageCandidate(const size_t bytes)  | 
| 39 |   { | 
| 40 |     if (!huge_pages_enabled) | 
| 41 |       return false; | 
| 42 |  | 
| 43 |     /* use huge pages only when memory overhead is low */ | 
| 44 |     const size_t hbytes = (bytes+PAGE_SIZE_2M-1) & ~size_t(PAGE_SIZE_2M-1); | 
| 45 |     return 66*(hbytes-bytes) < bytes; // at most 1.5% overhead | 
| 46 |   } | 
| 47 | } | 
| 48 |  | 
| 49 | //////////////////////////////////////////////////////////////////////////////// | 
| 50 | /// Windows Platform | 
| 51 | //////////////////////////////////////////////////////////////////////////////// | 
| 52 |  | 
| 53 | #ifdef _WIN32 | 
| 54 |  | 
| 55 | #define WIN32_LEAN_AND_MEAN | 
| 56 | #include <windows.h> | 
| 57 | #include <malloc.h> | 
| 58 |  | 
| 59 | namespace embree | 
| 60 | { | 
| 61 |   bool win_enable_selockmemoryprivilege (bool verbose) | 
| 62 |   { | 
| 63 |     HANDLE hToken; | 
| 64 |     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) { | 
| 65 |       if (verbose) std::cout << "WARNING: OpenProcessToken failed while trying to enable SeLockMemoryPrivilege: "  << GetLastError() << std::endl; | 
| 66 |       return false; | 
| 67 |     } | 
| 68 |  | 
| 69 |     TOKEN_PRIVILEGES tp; | 
| 70 |     tp.PrivilegeCount = 1; | 
| 71 |     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | 
| 72 |  | 
| 73 |     if (!LookupPrivilegeValueW(nullptr, L"SeLockMemoryPrivilege" , &tp.Privileges[0].Luid)) { | 
| 74 |       if (verbose) std::cout << "WARNING: LookupPrivilegeValue failed while trying to enable SeLockMemoryPrivilege: "  << GetLastError() << std::endl; | 
| 75 |       return false; | 
| 76 |     } | 
| 77 |      | 
| 78 |     SetLastError(ERROR_SUCCESS); | 
| 79 |     if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), nullptr, 0)) { | 
| 80 |       if (verbose) std::cout << "WARNING: AdjustTokenPrivileges failed while trying to enable SeLockMemoryPrivilege"  << std::endl; | 
| 81 |       return false; | 
| 82 |     } | 
| 83 |      | 
| 84 |     if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { | 
| 85 |       if (verbose) std::cout << "WARNING: AdjustTokenPrivileges failed to enable SeLockMemoryPrivilege: Add SeLockMemoryPrivilege for current user and run process in elevated mode (Run as administrator)."  << std::endl; | 
| 86 |       return false; | 
| 87 |     }  | 
| 88 |  | 
| 89 |     return true; | 
| 90 |   } | 
| 91 |  | 
| 92 |   bool os_init(bool hugepages, bool verbose)  | 
| 93 |   { | 
| 94 |     Lock<MutexSys> lock(os_init_mutex); | 
| 95 |  | 
| 96 |     if (!hugepages) { | 
| 97 |       huge_pages_enabled = false; | 
| 98 |       return true; | 
| 99 |     } | 
| 100 |  | 
| 101 |     if (GetLargePageMinimum() != PAGE_SIZE_2M) { | 
| 102 |       huge_pages_enabled = false; | 
| 103 |       return false; | 
| 104 |     } | 
| 105 |  | 
| 106 |     huge_pages_enabled = true; | 
| 107 |     return true; | 
| 108 |   } | 
| 109 |  | 
| 110 |   void* os_malloc(size_t bytes, bool& hugepages) | 
| 111 |   { | 
| 112 |     if (bytes == 0) { | 
| 113 |       hugepages = false; | 
| 114 |       return nullptr; | 
| 115 |     } | 
| 116 |  | 
| 117 |     /* try direct huge page allocation first */ | 
| 118 |     if (isHugePageCandidate(bytes))  | 
| 119 |     { | 
| 120 |       int flags = MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES; | 
| 121 |       char* ptr = (char*) VirtualAlloc(nullptr,bytes,flags,PAGE_READWRITE); | 
| 122 |       if (ptr != nullptr) { | 
| 123 |         hugepages = true; | 
| 124 |         return ptr; | 
| 125 |       } | 
| 126 |     }  | 
| 127 |  | 
| 128 |     /* fall back to 4k pages */ | 
| 129 |     int flags = MEM_COMMIT | MEM_RESERVE; | 
| 130 |     char* ptr = (char*) VirtualAlloc(nullptr,bytes,flags,PAGE_READWRITE); | 
| 131 |     if (ptr == nullptr) throw std::bad_alloc(); | 
| 132 |     hugepages = false; | 
| 133 |     return ptr; | 
| 134 |   } | 
| 135 |  | 
| 136 |   size_t os_shrink(void* ptr, size_t bytesNew, size_t bytesOld, bool hugepages)  | 
| 137 |   { | 
| 138 |     if (hugepages) // decommitting huge pages seems not to work under Windows | 
| 139 |       return bytesOld; | 
| 140 |  | 
| 141 |     const size_t pageSize = hugepages ? PAGE_SIZE_2M : PAGE_SIZE_4K; | 
| 142 |     bytesNew = (bytesNew+pageSize-1) & ~(pageSize-1); | 
| 143 |     bytesOld = (bytesOld+pageSize-1) & ~(pageSize-1); | 
| 144 |     if (bytesNew >= bytesOld) | 
| 145 |       return bytesOld; | 
| 146 |  | 
| 147 |     if (!VirtualFree((char*)ptr+bytesNew,bytesOld-bytesNew,MEM_DECOMMIT)) | 
| 148 |       throw std::bad_alloc(); | 
| 149 |  | 
| 150 |     return bytesNew; | 
| 151 |   } | 
| 152 |  | 
| 153 |   void os_free(void* ptr, size_t bytes, bool hugepages)  | 
| 154 |   { | 
| 155 |     if (bytes == 0)  | 
| 156 |       return; | 
| 157 |  | 
| 158 |     if (!VirtualFree(ptr,0,MEM_RELEASE)) | 
| 159 |       throw std::bad_alloc(); | 
| 160 |   } | 
| 161 |  | 
| 162 |   void os_advise(void *ptr, size_t bytes) | 
| 163 |   { | 
| 164 |   } | 
| 165 | } | 
| 166 |  | 
| 167 | #endif | 
| 168 |  | 
| 169 | //////////////////////////////////////////////////////////////////////////////// | 
| 170 | /// Unix Platform | 
| 171 | //////////////////////////////////////////////////////////////////////////////// | 
| 172 |  | 
| 173 | #if defined(__UNIX__) | 
| 174 |  | 
| 175 | #include <sys/mman.h> | 
| 176 | #include <errno.h> | 
| 177 | #include <stdlib.h> | 
| 178 | #include <string.h> | 
| 179 | #include <sstream> | 
| 180 |  | 
| 181 | #if defined(__MACOSX__) | 
| 182 | #include <mach/vm_statistics.h> | 
| 183 | #endif | 
| 184 |  | 
| 185 | namespace embree | 
| 186 | { | 
| 187 |   bool os_init(bool hugepages, bool verbose)  | 
| 188 |   { | 
| 189 |     Lock<MutexSys> lock(os_init_mutex); | 
| 190 |  | 
| 191 |     if (!hugepages) { | 
| 192 |       huge_pages_enabled = false; | 
| 193 |       return true; | 
| 194 |     } | 
| 195 |  | 
| 196 | #if defined(__LINUX__) | 
| 197 |  | 
| 198 |     int hugepagesize = 0; | 
| 199 |  | 
| 200 |     std::ifstream file;  | 
| 201 |     file.open(s: "/proc/meminfo" ,mode: std::ios::in); | 
| 202 |     if (!file.is_open()) { | 
| 203 |       if (verbose) std::cout << "WARNING: Could not open /proc/meminfo. Huge page support cannot get enabled!"  << std::endl; | 
| 204 |       huge_pages_enabled = false; | 
| 205 |       return false; | 
| 206 |     } | 
| 207 |      | 
| 208 |     std::string line; | 
| 209 |     while (getline(is&: file,str&: line)) | 
| 210 |     { | 
| 211 |       std::stringstream sline(line); | 
| 212 |       while (!sline.eof() && sline.peek() == ' ') sline.ignore(); | 
| 213 |       std::string tag; getline(in&: sline,str&: tag,delim: ' '); | 
| 214 |       while (!sline.eof() && sline.peek() == ' ') sline.ignore(); | 
| 215 |       std::string val; getline(in&: sline,str&: val,delim: ' '); | 
| 216 |       while (!sline.eof() && sline.peek() == ' ') sline.ignore(); | 
| 217 |       std::string unit; getline(in&: sline,str&: unit,delim: ' '); | 
| 218 |       if (tag == "Hugepagesize:"  && unit == "kB" ) { | 
| 219 | 	hugepagesize = std::stoi(str: val)*1024; | 
| 220 | 	break; | 
| 221 |       } | 
| 222 |     } | 
| 223 |      | 
| 224 |     if (hugepagesize != PAGE_SIZE_2M)  | 
| 225 |     { | 
| 226 |       if (verbose) std::cout << "WARNING: Only 2MB huge pages supported. Huge page support cannot get enabled!"  << std::endl; | 
| 227 |       huge_pages_enabled = false; | 
| 228 |       return false; | 
| 229 |     } | 
| 230 | #endif | 
| 231 |  | 
| 232 |     huge_pages_enabled = true; | 
| 233 |     return true; | 
| 234 |   } | 
| 235 |  | 
| 236 |   void* os_malloc(size_t bytes, bool& hugepages) | 
| 237 |   {  | 
| 238 |     if (bytes == 0) { | 
| 239 |       hugepages = false; | 
| 240 |       return nullptr; | 
| 241 |     } | 
| 242 |  | 
| 243 |     /* try direct huge page allocation first */ | 
| 244 |     if (isHugePageCandidate(bytes))  | 
| 245 |     { | 
| 246 | #if defined(__MACOSX__) | 
| 247 |       void* ptr = mmap(0, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); | 
| 248 |       if (ptr != MAP_FAILED) { | 
| 249 |         hugepages = true; | 
| 250 |         return ptr; | 
| 251 |       } | 
| 252 | #elif defined(MAP_HUGETLB) | 
| 253 |       void* ptr = mmap(addr: 0, len: bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_HUGETLB, fd: -1, offset: 0); | 
| 254 |       if (ptr != MAP_FAILED) { | 
| 255 |         hugepages = true; | 
| 256 |         return ptr; | 
| 257 |       } | 
| 258 | #endif | 
| 259 |     }  | 
| 260 |  | 
| 261 |     /* fallback to 4k pages */ | 
| 262 |     void* ptr = (char*) mmap(addr: 0, len: bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, fd: -1, offset: 0); | 
| 263 |     if (ptr == MAP_FAILED) throw std::bad_alloc(); | 
| 264 |     hugepages = false; | 
| 265 |  | 
| 266 |     /* advise huge page hint for THP */ | 
| 267 |     os_advise(ptr,bytes); | 
| 268 |     return ptr; | 
| 269 |   } | 
| 270 |  | 
| 271 |   size_t os_shrink(void* ptr, size_t bytesNew, size_t bytesOld, bool hugepages)  | 
| 272 |   { | 
| 273 |     const size_t pageSize = hugepages ? PAGE_SIZE_2M : PAGE_SIZE_4K; | 
| 274 |     bytesNew = (bytesNew+pageSize-1) & ~(pageSize-1); | 
| 275 |     bytesOld = (bytesOld+pageSize-1) & ~(pageSize-1); | 
| 276 |     if (bytesNew >= bytesOld) | 
| 277 |       return bytesOld; | 
| 278 |  | 
| 279 |     if (munmap(addr: (char*)ptr+bytesNew,len: bytesOld-bytesNew) == -1) | 
| 280 |       throw std::bad_alloc(); | 
| 281 |  | 
| 282 |     return bytesNew; | 
| 283 |   } | 
| 284 |  | 
| 285 |   void os_free(void* ptr, size_t bytes, bool hugepages)  | 
| 286 |   { | 
| 287 |     if (bytes == 0) | 
| 288 |       return; | 
| 289 |  | 
| 290 |     /* for hugepages we need to also align the size */ | 
| 291 |     const size_t pageSize = hugepages ? PAGE_SIZE_2M : PAGE_SIZE_4K; | 
| 292 |     bytes = (bytes+pageSize-1) & ~(pageSize-1); | 
| 293 |     if (munmap(addr: ptr,len: bytes) == -1) | 
| 294 |       throw std::bad_alloc(); | 
| 295 |   } | 
| 296 |  | 
| 297 |   /* hint for transparent huge pages (THP) */ | 
| 298 |   void os_advise(void* pptr, size_t bytes) | 
| 299 |   { | 
| 300 | #if defined(MADV_HUGEPAGE) | 
| 301 |     madvise(addr: pptr,len: bytes,MADV_HUGEPAGE);  | 
| 302 | #endif | 
| 303 |   } | 
| 304 | } | 
| 305 |  | 
| 306 | #endif | 
| 307 |  |