|
21 | 21 | // SPDX-License-Identifier: MIT
|
22 | 22 |
|
23 | 23 | #include "node_large_page.h"
|
| 24 | +#include "util.h" |
| 25 | +#include "uv.h" |
24 | 26 |
|
25 | 27 | #include <fcntl.h> // _O_RDWR
|
26 | 28 | #include <sys/types.h>
|
27 | 29 | #include <sys/mman.h>
|
| 30 | +#if defined(__FreeBSD__) |
| 31 | +#include <sys/sysctl.h> |
| 32 | +#include <sys/user.h> |
| 33 | +#endif |
28 | 34 | #include <unistd.h> // readlink
|
29 | 35 |
|
30 | 36 | #include <cerrno> // NOLINT(build/include)
|
|
39 | 45 | #include <fstream>
|
40 | 46 | #include <iostream>
|
41 | 47 | #include <sstream>
|
| 48 | +#include <vector> |
42 | 49 |
|
43 | 50 | // The functions in this file map the text segment of node into 2M pages.
|
44 | 51 | // The algorithm is simple
|
@@ -88,6 +95,7 @@ inline int64_t hugepage_align_down(int64_t addr) {
|
88 | 95 | // This is also handling the case where the first line is not the binary
|
89 | 96 |
|
90 | 97 | static struct text_region FindNodeTextRegion() {
|
| 98 | +#if defined(__linux__) |
91 | 99 | std::ifstream ifs;
|
92 | 100 | std::string map_line;
|
93 | 101 | std::string permission;
|
@@ -141,9 +149,68 @@ static struct text_region FindNodeTextRegion() {
|
141 | 149 | }
|
142 | 150 |
|
143 | 151 | ifs.close();
|
| 152 | +#elif defined(__FreeBSD__) |
| 153 | + struct text_region nregion; |
| 154 | + nregion.found_text_region = false; |
| 155 | + |
| 156 | + std::string exename; |
| 157 | + { |
| 158 | + char selfexe[PATH_MAX]; |
| 159 | + size_t count = sizeof(selfexe); |
| 160 | + if (uv_exepath(selfexe, &count)) |
| 161 | + return nregion; |
| 162 | + |
| 163 | + exename = std::string(selfexe, count); |
| 164 | + } |
| 165 | + |
| 166 | + size_t numpg; |
| 167 | + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; |
| 168 | + const size_t miblen = arraysize(mib); |
| 169 | + if (sysctl(mib, miblen, nullptr, &numpg, nullptr, 0) == -1) { |
| 170 | + return nregion; |
| 171 | + } |
| 172 | + |
| 173 | + // for struct kinfo_vmentry |
| 174 | + numpg = numpg * 4 / 3; |
| 175 | + auto alg = std::vector<char>(numpg); |
| 176 | + |
| 177 | + if (sysctl(mib, miblen, alg.data(), &numpg, nullptr, 0) == -1) { |
| 178 | + return nregion; |
| 179 | + } |
| 180 | + |
| 181 | + char* start = alg.data(); |
| 182 | + char* end = start + numpg; |
| 183 | + |
| 184 | + while (start < end) { |
| 185 | + kinfo_vmentry* entry = reinterpret_cast<kinfo_vmentry*>(start); |
| 186 | + const size_t cursz = entry->kve_structsize; |
| 187 | + if (cursz == 0) { |
| 188 | + break; |
| 189 | + } |
| 190 | + |
| 191 | + if (entry->kve_path[0] == '\0') { |
| 192 | + continue; |
| 193 | + } |
| 194 | + bool excmapping = ((entry->kve_protection & KVME_PROT_READ) && |
| 195 | + (entry->kve_protection & KVME_PROT_EXEC)); |
| 196 | + |
| 197 | + if (!strcmp(exename.c_str(), entry->kve_path) && excmapping) { |
| 198 | + size_t size = entry->kve_end - entry->kve_start; |
| 199 | + nregion.found_text_region = true; |
| 200 | + nregion.from = |
| 201 | + reinterpret_cast<char*>(hugepage_align_up(entry->kve_start)); |
| 202 | + nregion.to = |
| 203 | + reinterpret_cast<char*>(hugepage_align_down(entry->kve_end)); |
| 204 | + nregion.total_hugepages = size / hps; |
| 205 | + break; |
| 206 | + } |
| 207 | + start += cursz; |
| 208 | + } |
| 209 | +#endif |
144 | 210 | return nregion;
|
145 | 211 | }
|
146 | 212 |
|
| 213 | +#if defined(__linux__) |
147 | 214 | static bool IsTransparentHugePagesEnabled() {
|
148 | 215 | std::ifstream ifs;
|
149 | 216 |
|
@@ -171,6 +238,19 @@ static bool IsTransparentHugePagesEnabled() {
|
171 | 238 | ifs.close();
|
172 | 239 | return ret_status;
|
173 | 240 | }
|
| 241 | +#elif defined(__FreeBSD__) |
| 242 | +static bool IsSuperPagesEnabled() { |
| 243 | + // It is enabled by default on amd64 |
| 244 | + unsigned int super_pages = 0; |
| 245 | + size_t super_pages_length = sizeof(super_pages); |
| 246 | + if (sysctlbyname("vm.pmap.pg_ps_enabled", &super_pages, |
| 247 | + &super_pages_length, nullptr, 0) == -1 || |
| 248 | + super_pages < 1) { |
| 249 | + return false; |
| 250 | + } |
| 251 | + return true; |
| 252 | +} |
| 253 | +#endif |
174 | 254 |
|
175 | 255 | // Moving the text region to large pages. We need to be very careful.
|
176 | 256 | // 1: This function itself should not be moved.
|
@@ -206,6 +286,7 @@ MoveTextRegionToLargePages(const text_region& r) {
|
206 | 286 |
|
207 | 287 | memcpy(nmem, r.from, size);
|
208 | 288 |
|
| 289 | +#if defined(__linux__) |
209 | 290 | // We already know the original page is r-xp
|
210 | 291 | // (PROT_READ, PROT_EXEC, MAP_PRIVATE)
|
211 | 292 | // We want PROT_WRITE because we are writing into it.
|
@@ -233,6 +314,17 @@ MoveTextRegionToLargePages(const text_region& r) {
|
233 | 314 |
|
234 | 315 | return -1;
|
235 | 316 | }
|
| 317 | +#elif defined(__FreeBSD__) |
| 318 | + tmem = mmap(start, size, |
| 319 | + PROT_READ | PROT_WRITE | PROT_EXEC, |
| 320 | + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | |
| 321 | + MAP_ALIGNED_SUPER, -1 , 0); |
| 322 | + if (tmem == MAP_FAILED) { |
| 323 | + PrintSystemError(errno); |
| 324 | + munmap(nmem, size); |
| 325 | + return -1; |
| 326 | + } |
| 327 | +#endif |
236 | 328 |
|
237 | 329 | memcpy(start, nmem, size);
|
238 | 330 | ret = mprotect(start, size, PROT_READ | PROT_EXEC);
|
@@ -266,14 +358,22 @@ int MapStaticCodeToLargePages() {
|
266 | 358 | return -1;
|
267 | 359 | }
|
268 | 360 |
|
| 361 | +#if defined(__linux__) |
269 | 362 | if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
|
270 | 363 | return MoveTextRegionToLargePages(r);
|
271 | 364 |
|
272 | 365 | return -1;
|
| 366 | +#elif defined(__FreeBSD__) |
| 367 | + return MoveTextRegionToLargePages(r); |
| 368 | +#endif |
273 | 369 | }
|
274 | 370 |
|
275 | 371 | bool IsLargePagesEnabled() {
|
| 372 | +#if defined(__linux__) |
276 | 373 | return IsTransparentHugePagesEnabled();
|
| 374 | +#else |
| 375 | + return IsSuperPagesEnabled(); |
| 376 | +#endif |
277 | 377 | }
|
278 | 378 |
|
279 | 379 | } // namespace node
|
0 commit comments