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;