Mercurial > hg > graal-jvmci-8
comparison src/os/linux/vm/os_linux.cpp @ 24006:44c8fe602a5e
8077276: allocating heap with UseLargePages and HugeTLBFS may trash existing memory mappings (linux)
Summary: Remove MAP_FIXED from initial mapping allocation; add tests
Reviewed-by: stefank, coleenp
author | stuefe |
---|---|
date | Thu, 23 Apr 2015 18:00:50 +0200 |
parents | 626f594dffa6 |
children | 776cb7cbe2e4 |
comparison
equal
deleted
inserted
replaced
24005:79351ea143ee | 24006:44c8fe602a5e |
---|---|
3431 assert(is_ptr_aligned(addr, os::large_page_size()), "Must be"); | 3431 assert(is_ptr_aligned(addr, os::large_page_size()), "Must be"); |
3432 | 3432 |
3433 return addr; | 3433 return addr; |
3434 } | 3434 } |
3435 | 3435 |
3436 // Helper for os::Linux::reserve_memory_special_huge_tlbfs_mixed(). | |
3437 // Allocate (using mmap, NO_RESERVE, with small pages) at either a given request address | |
3438 // (req_addr != NULL) or with a given alignment. | |
3439 // - bytes shall be a multiple of alignment. | |
3440 // - req_addr can be NULL. If not NULL, it must be a multiple of alignment. | |
3441 // - alignment sets the alignment at which memory shall be allocated. | |
3442 // It must be a multiple of allocation granularity. | |
3443 // Returns address of memory or NULL. If req_addr was not NULL, will only return | |
3444 // req_addr or NULL. | |
3445 static char* anon_mmap_aligned(size_t bytes, size_t alignment, char* req_addr) { | |
3446 | |
3447 size_t extra_size = bytes; | |
3448 if (req_addr == NULL && alignment > 0) { | |
3449 extra_size += alignment; | |
3450 } | |
3451 | |
3452 char* start = (char*) ::mmap(req_addr, extra_size, PROT_NONE, | |
3453 MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, | |
3454 -1, 0); | |
3455 if (start == MAP_FAILED) { | |
3456 start = NULL; | |
3457 } else { | |
3458 if (req_addr != NULL) { | |
3459 if (start != req_addr) { | |
3460 ::munmap(start, extra_size); | |
3461 start = NULL; | |
3462 } | |
3463 } else { | |
3464 char* const start_aligned = (char*) align_ptr_up(start, alignment); | |
3465 char* const end_aligned = start_aligned + bytes; | |
3466 char* const end = start + extra_size; | |
3467 if (start_aligned > start) { | |
3468 ::munmap(start, start_aligned - start); | |
3469 } | |
3470 if (end_aligned < end) { | |
3471 ::munmap(end_aligned, end - end_aligned); | |
3472 } | |
3473 start = start_aligned; | |
3474 } | |
3475 } | |
3476 return start; | |
3477 | |
3478 } | |
3479 | |
3480 // Reserve memory using mmap(MAP_HUGETLB). | |
3481 // - bytes shall be a multiple of alignment. | |
3482 // - req_addr can be NULL. If not NULL, it must be a multiple of alignment. | |
3483 // - alignment sets the alignment at which memory shall be allocated. | |
3484 // It must be a multiple of allocation granularity. | |
3485 // Returns address of memory or NULL. If req_addr was not NULL, will only return | |
3486 // req_addr or NULL. | |
3436 char* os::Linux::reserve_memory_special_huge_tlbfs_mixed(size_t bytes, size_t alignment, char* req_addr, bool exec) { | 3487 char* os::Linux::reserve_memory_special_huge_tlbfs_mixed(size_t bytes, size_t alignment, char* req_addr, bool exec) { |
3437 size_t large_page_size = os::large_page_size(); | 3488 size_t large_page_size = os::large_page_size(); |
3438 | |
3439 assert(bytes >= large_page_size, "Shouldn't allocate large pages for small sizes"); | 3489 assert(bytes >= large_page_size, "Shouldn't allocate large pages for small sizes"); |
3440 | 3490 |
3441 // Allocate small pages. | 3491 assert(is_ptr_aligned(req_addr, alignment), "Must be"); |
3442 | 3492 assert(is_size_aligned(bytes, alignment), "Must be"); |
3443 char* start; | 3493 |
3444 if (req_addr != NULL) { | 3494 // First reserve - but not commit - the address range in small pages. |
3445 assert(is_ptr_aligned(req_addr, alignment), "Must be"); | 3495 char* const start = anon_mmap_aligned(bytes, alignment, req_addr); |
3446 assert(is_size_aligned(bytes, alignment), "Must be"); | |
3447 start = os::reserve_memory(bytes, req_addr); | |
3448 assert(start == NULL || start == req_addr, "Must be"); | |
3449 } else { | |
3450 start = os::reserve_memory_aligned(bytes, alignment); | |
3451 } | |
3452 | 3496 |
3453 if (start == NULL) { | 3497 if (start == NULL) { |
3454 return NULL; | 3498 return NULL; |
3455 } | 3499 } |
3456 | 3500 |
3457 assert(is_ptr_aligned(start, alignment), "Must be"); | 3501 assert(is_ptr_aligned(start, alignment), "Must be"); |
3458 | |
3459 if (MemTracker::tracking_level() > NMT_minimal) { | |
3460 // os::reserve_memory_special will record this memory area. | |
3461 // Need to release it here to prevent overlapping reservations. | |
3462 Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); | |
3463 tkr.record((address)start, bytes); | |
3464 } | |
3465 | 3502 |
3466 char* end = start + bytes; | 3503 char* end = start + bytes; |
3467 | 3504 |
3468 // Find the regions of the allocated chunk that can be promoted to large pages. | 3505 // Find the regions of the allocated chunk that can be promoted to large pages. |
3469 char* lp_start = (char*)align_ptr_up(start, large_page_size); | 3506 char* lp_start = (char*)align_ptr_up(start, large_page_size); |
3480 return NULL; | 3517 return NULL; |
3481 } | 3518 } |
3482 | 3519 |
3483 int prot = exec ? PROT_READ|PROT_WRITE|PROT_EXEC : PROT_READ|PROT_WRITE; | 3520 int prot = exec ? PROT_READ|PROT_WRITE|PROT_EXEC : PROT_READ|PROT_WRITE; |
3484 | 3521 |
3485 | |
3486 void* result; | 3522 void* result; |
3487 | 3523 |
3524 // Commit small-paged leading area. | |
3488 if (start != lp_start) { | 3525 if (start != lp_start) { |
3489 result = ::mmap(start, lp_start - start, prot, | 3526 result = ::mmap(start, lp_start - start, prot, |
3490 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, | 3527 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, |
3491 -1, 0); | 3528 -1, 0); |
3492 if (result == MAP_FAILED) { | 3529 if (result == MAP_FAILED) { |
3493 ::munmap(lp_start, end - lp_start); | 3530 ::munmap(lp_start, end - lp_start); |
3494 return NULL; | 3531 return NULL; |
3495 } | 3532 } |
3496 } | 3533 } |
3497 | 3534 |
3535 // Commit large-paged area. | |
3498 result = ::mmap(lp_start, lp_bytes, prot, | 3536 result = ::mmap(lp_start, lp_bytes, prot, |
3499 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_HUGETLB, | 3537 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_HUGETLB, |
3500 -1, 0); | 3538 -1, 0); |
3501 if (result == MAP_FAILED) { | 3539 if (result == MAP_FAILED) { |
3502 warn_on_large_pages_failure(req_addr, bytes, errno); | 3540 warn_on_large_pages_failure(lp_start, lp_bytes, errno); |
3503 // If the mmap above fails, the large pages region will be unmapped and we | 3541 // If the mmap above fails, the large pages region will be unmapped and we |
3504 // have regions before and after with small pages. Release these regions. | 3542 // have regions before and after with small pages. Release these regions. |
3505 // | 3543 // |
3506 // | mapped | unmapped | mapped | | 3544 // | mapped | unmapped | mapped | |
3507 // ^ ^ ^ ^ | 3545 // ^ ^ ^ ^ |
3510 ::munmap(start, lp_start - start); | 3548 ::munmap(start, lp_start - start); |
3511 ::munmap(lp_end, end - lp_end); | 3549 ::munmap(lp_end, end - lp_end); |
3512 return NULL; | 3550 return NULL; |
3513 } | 3551 } |
3514 | 3552 |
3553 // Commit small-paged trailing area. | |
3515 if (lp_end != end) { | 3554 if (lp_end != end) { |
3516 result = ::mmap(lp_end, end - lp_end, prot, | 3555 result = ::mmap(lp_end, end - lp_end, prot, |
3517 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, | 3556 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, |
3518 -1, 0); | 3557 -1, 0); |
3519 if (result == MAP_FAILED) { | 3558 if (result == MAP_FAILED) { |
3526 } | 3565 } |
3527 | 3566 |
3528 char* os::Linux::reserve_memory_special_huge_tlbfs(size_t bytes, size_t alignment, char* req_addr, bool exec) { | 3567 char* os::Linux::reserve_memory_special_huge_tlbfs(size_t bytes, size_t alignment, char* req_addr, bool exec) { |
3529 assert(UseLargePages && UseHugeTLBFS, "only for Huge TLBFS large pages"); | 3568 assert(UseLargePages && UseHugeTLBFS, "only for Huge TLBFS large pages"); |
3530 assert(is_ptr_aligned(req_addr, alignment), "Must be"); | 3569 assert(is_ptr_aligned(req_addr, alignment), "Must be"); |
3531 assert(is_power_of_2(alignment), "Must be"); | 3570 assert(is_size_aligned(alignment, os::vm_allocation_granularity()), "Must be"); |
3532 assert(is_power_of_2(os::large_page_size()), "Must be"); | 3571 assert(is_power_of_2(os::large_page_size()), "Must be"); |
3533 assert(bytes >= os::large_page_size(), "Shouldn't allocate large pages for small sizes"); | 3572 assert(bytes >= os::large_page_size(), "Shouldn't allocate large pages for small sizes"); |
3534 | 3573 |
3535 if (is_size_aligned(bytes, os::large_page_size()) && alignment <= os::large_page_size()) { | 3574 if (is_size_aligned(bytes, os::large_page_size()) && alignment <= os::large_page_size()) { |
3536 return reserve_memory_special_huge_tlbfs_only(bytes, req_addr, exec); | 3575 return reserve_memory_special_huge_tlbfs_only(bytes, req_addr, exec); |
6100 for (size_t size = lp; size <= lp * 10; size += lp) { | 6139 for (size_t size = lp; size <= lp * 10; size += lp) { |
6101 test_reserve_memory_special_huge_tlbfs_only(size); | 6140 test_reserve_memory_special_huge_tlbfs_only(size); |
6102 } | 6141 } |
6103 } | 6142 } |
6104 | 6143 |
6105 static void test_reserve_memory_special_huge_tlbfs_mixed(size_t size, size_t alignment) { | |
6106 if (!UseHugeTLBFS) { | |
6107 return; | |
6108 } | |
6109 | |
6110 test_log("test_reserve_memory_special_huge_tlbfs_mixed(" SIZE_FORMAT ", " SIZE_FORMAT ")", | |
6111 size, alignment); | |
6112 | |
6113 assert(size >= os::large_page_size(), "Incorrect input to test"); | |
6114 | |
6115 char* addr = os::Linux::reserve_memory_special_huge_tlbfs_mixed(size, alignment, NULL, false); | |
6116 | |
6117 if (addr != NULL) { | |
6118 small_page_write(addr, size); | |
6119 | |
6120 os::Linux::release_memory_special_huge_tlbfs(addr, size); | |
6121 } | |
6122 } | |
6123 | |
6124 static void test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(size_t size) { | |
6125 size_t lp = os::large_page_size(); | |
6126 size_t ag = os::vm_allocation_granularity(); | |
6127 | |
6128 for (size_t alignment = ag; is_size_aligned(size, alignment); alignment *= 2) { | |
6129 test_reserve_memory_special_huge_tlbfs_mixed(size, alignment); | |
6130 } | |
6131 } | |
6132 | |
6133 static void test_reserve_memory_special_huge_tlbfs_mixed() { | 6144 static void test_reserve_memory_special_huge_tlbfs_mixed() { |
6134 size_t lp = os::large_page_size(); | 6145 size_t lp = os::large_page_size(); |
6135 size_t ag = os::vm_allocation_granularity(); | 6146 size_t ag = os::vm_allocation_granularity(); |
6136 | 6147 |
6137 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp); | 6148 // sizes to test |
6138 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp + ag); | 6149 const size_t sizes[] = { |
6139 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp + lp / 2); | 6150 lp, lp + ag, lp + lp / 2, lp * 2, |
6140 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp * 2); | 6151 lp * 2 + ag, lp * 2 - ag, lp * 2 + lp / 2, |
6141 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp * 2 + ag); | 6152 lp * 10, lp * 10 + lp / 2 |
6142 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp * 2 - ag); | 6153 }; |
6143 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp * 2 + lp / 2); | 6154 const int num_sizes = sizeof(sizes) / sizeof(size_t); |
6144 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp * 10); | 6155 |
6145 test_reserve_memory_special_huge_tlbfs_mixed_all_alignments(lp * 10 + lp / 2); | 6156 // For each size/alignment combination, we test three scenarios: |
6157 // 1) with req_addr == NULL | |
6158 // 2) with a non-null req_addr at which we expect to successfully allocate | |
6159 // 3) with a non-null req_addr which contains a pre-existing mapping, at which we | |
6160 // expect the allocation to either fail or to ignore req_addr | |
6161 | |
6162 // Pre-allocate two areas; they shall be as large as the largest allocation | |
6163 // and aligned to the largest alignment we will be testing. | |
6164 const size_t mapping_size = sizes[num_sizes - 1] * 2; | |
6165 char* const mapping1 = (char*) ::mmap(NULL, mapping_size, | |
6166 PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, | |
6167 -1, 0); | |
6168 assert(mapping1 != MAP_FAILED, "should work"); | |
6169 | |
6170 char* const mapping2 = (char*) ::mmap(NULL, mapping_size, | |
6171 PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, | |
6172 -1, 0); | |
6173 assert(mapping2 != MAP_FAILED, "should work"); | |
6174 | |
6175 // Unmap the first mapping, but leave the second mapping intact: the first | |
6176 // mapping will serve as a value for a "good" req_addr (case 2). The second | |
6177 // mapping, still intact, as "bad" req_addr (case 3). | |
6178 ::munmap(mapping1, mapping_size); | |
6179 | |
6180 // Case 1 | |
6181 test_log("%s, req_addr NULL:", __FUNCTION__); | |
6182 test_log("size align result"); | |
6183 | |
6184 for (int i = 0; i < num_sizes; i++) { | |
6185 const size_t size = sizes[i]; | |
6186 for (size_t alignment = ag; is_size_aligned(size, alignment); alignment *= 2) { | |
6187 char* p = os::Linux::reserve_memory_special_huge_tlbfs_mixed(size, alignment, NULL, false); | |
6188 test_log(SIZE_FORMAT_HEX " " SIZE_FORMAT_HEX " -> " PTR_FORMAT " %s", | |
6189 size, alignment, p, (p != NULL ? "" : "(failed)")); | |
6190 if (p != NULL) { | |
6191 assert(is_ptr_aligned(p, alignment), "must be"); | |
6192 small_page_write(p, size); | |
6193 os::Linux::release_memory_special_huge_tlbfs(p, size); | |
6194 } | |
6195 } | |
6196 } | |
6197 | |
6198 // Case 2 | |
6199 test_log("%s, req_addr non-NULL:", __FUNCTION__); | |
6200 test_log("size align req_addr result"); | |
6201 | |
6202 for (int i = 0; i < num_sizes; i++) { | |
6203 const size_t size = sizes[i]; | |
6204 for (size_t alignment = ag; is_size_aligned(size, alignment); alignment *= 2) { | |
6205 char* const req_addr = (char*) align_ptr_up(mapping1, alignment); | |
6206 char* p = os::Linux::reserve_memory_special_huge_tlbfs_mixed(size, alignment, req_addr, false); | |
6207 test_log(SIZE_FORMAT_HEX " " SIZE_FORMAT_HEX " " PTR_FORMAT " -> " PTR_FORMAT " %s", | |
6208 size, alignment, req_addr, p, | |
6209 ((p != NULL ? (p == req_addr ? "(exact match)" : "") : "(failed)"))); | |
6210 if (p != NULL) { | |
6211 assert(p == req_addr, "must be"); | |
6212 small_page_write(p, size); | |
6213 os::Linux::release_memory_special_huge_tlbfs(p, size); | |
6214 } | |
6215 } | |
6216 } | |
6217 | |
6218 // Case 3 | |
6219 test_log("%s, req_addr non-NULL with preexisting mapping:", __FUNCTION__); | |
6220 test_log("size align req_addr result"); | |
6221 | |
6222 for (int i = 0; i < num_sizes; i++) { | |
6223 const size_t size = sizes[i]; | |
6224 for (size_t alignment = ag; is_size_aligned(size, alignment); alignment *= 2) { | |
6225 char* const req_addr = (char*) align_ptr_up(mapping2, alignment); | |
6226 char* p = os::Linux::reserve_memory_special_huge_tlbfs_mixed(size, alignment, req_addr, false); | |
6227 test_log(SIZE_FORMAT_HEX " " SIZE_FORMAT_HEX " " PTR_FORMAT " -> " PTR_FORMAT " %s", | |
6228 size, alignment, req_addr, p, | |
6229 ((p != NULL ? "" : "(failed)"))); | |
6230 // as the area around req_addr contains already existing mappings, the API should always | |
6231 // return NULL (as per contract, it cannot return another address) | |
6232 assert(p == NULL, "must be"); | |
6233 } | |
6234 } | |
6235 | |
6236 ::munmap(mapping2, mapping_size); | |
6237 | |
6146 } | 6238 } |
6147 | 6239 |
6148 static void test_reserve_memory_special_huge_tlbfs() { | 6240 static void test_reserve_memory_special_huge_tlbfs() { |
6149 if (!UseHugeTLBFS) { | 6241 if (!UseHugeTLBFS) { |
6150 return; | 6242 return; |