mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-17 09:34:38 +02:00

Co-authored-by: Stefan Karlsson <stefank@openjdk.org> Co-authored-by: Erik Österlund <eosterlund@openjdk.org> Co-authored-by: Axel Boldt-Christmas <aboldtch@openjdk.org> Co-authored-by: Per Liden <pliden@openjdk.org> Co-authored-by: Stefan Johansson <sjohanss@openjdk.org> Co-authored-by: Albert Mingkun Yang <ayang@openjdk.org> Co-authored-by: Erik Helin <ehelin@openjdk.org> Co-authored-by: Roberto Castañeda Lozano <rcastanedalo@openjdk.org> Co-authored-by: Nils Eliasson <neliasso@openjdk.org> Co-authored-by: Martin Doerr <mdoerr@openjdk.org> Co-authored-by: Leslie Zhai <lzhai@openjdk.org> Co-authored-by: Fei Yang <fyang@openjdk.org> Co-authored-by: Yadong Wang <yadongwang@openjdk.org> Reviewed-by: eosterlund, aboldtch, rcastanedalo
310 lines
11 KiB
C++
310 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
#include "precompiled.hpp"
|
|
#include "gc/x/xMapper_windows.hpp"
|
|
#include "gc/x/xSyscall_windows.hpp"
|
|
#include "logging/log.hpp"
|
|
#include "utilities/debug.hpp"
|
|
|
|
#include <Windows.h>
|
|
|
|
// Memory reservation, commit, views, and placeholders.
|
|
//
|
|
// To be able to up-front reserve address space for the heap views, and later
|
|
// multi-map the heap views to the same physical memory, without ever losing the
|
|
// reservation of the reserved address space, we use "placeholders".
|
|
//
|
|
// These placeholders block out the address space from being used by other parts
|
|
// of the process. To commit memory in this address space, the placeholder must
|
|
// be replaced by anonymous memory, or replaced by mapping a view against a
|
|
// paging file mapping. We use the later to support multi-mapping.
|
|
//
|
|
// We want to be able to dynamically commit and uncommit the physical memory of
|
|
// the heap (and also unmap ZPages), in granules of ZGranuleSize bytes. There is
|
|
// no way to grow and shrink the committed memory of a paging file mapping.
|
|
// Therefore, we create multiple granule-sized page file mappings. The memory is
|
|
// committed by creating a page file mapping, map a view against it, commit the
|
|
// memory, unmap the view. The memory will stay committed until all views are
|
|
// unmapped, and the paging file mapping handle is closed.
|
|
//
|
|
// When replacing a placeholder address space reservation with a mapped view
|
|
// against a paging file mapping, the virtual address space must exactly match
|
|
// an existing placeholder's address and size. Therefore we only deal with
|
|
// granule-sized placeholders at this layer. Higher layers that keep track of
|
|
// reserved available address space can (and will) coalesce placeholders, but
|
|
// they will be split before being used.
|
|
|
|
#define fatal_error(msg, addr, size) \
|
|
fatal(msg ": " PTR_FORMAT " " SIZE_FORMAT "M (%d)", \
|
|
(addr), (size) / M, GetLastError())
|
|
|
|
uintptr_t XMapper::reserve(uintptr_t addr, size_t size) {
|
|
void* const res = XSyscall::VirtualAlloc2(
|
|
GetCurrentProcess(), // Process
|
|
(void*)addr, // BaseAddress
|
|
size, // Size
|
|
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, // AllocationType
|
|
PAGE_NOACCESS, // PageProtection
|
|
nullptr, // ExtendedParameters
|
|
0 // ParameterCount
|
|
);
|
|
|
|
// Caller responsible for error handling
|
|
return (uintptr_t)res;
|
|
}
|
|
|
|
void XMapper::unreserve(uintptr_t addr, size_t size) {
|
|
const bool res = XSyscall::VirtualFreeEx(
|
|
GetCurrentProcess(), // hProcess
|
|
(void*)addr, // lpAddress
|
|
size, // dwSize
|
|
MEM_RELEASE // dwFreeType
|
|
);
|
|
|
|
if (!res) {
|
|
fatal_error("Failed to unreserve memory", addr, size);
|
|
}
|
|
}
|
|
|
|
HANDLE XMapper::create_paging_file_mapping(size_t size) {
|
|
// Create mapping with SEC_RESERVE instead of SEC_COMMIT.
|
|
//
|
|
// We use MapViewOfFile3 for two different reasons:
|
|
// 1) When committing memory for the created paging file
|
|
// 2) When mapping a view of the memory created in (2)
|
|
//
|
|
// The non-platform code is only setup to deal with out-of-memory
|
|
// errors in (1). By using SEC_RESERVE, we prevent MapViewOfFile3
|
|
// from failing because of "commit limit" checks. To actually commit
|
|
// memory in (1), a call to VirtualAlloc2 is done.
|
|
|
|
HANDLE const res = XSyscall::CreateFileMappingW(
|
|
INVALID_HANDLE_VALUE, // hFile
|
|
nullptr, // lpFileMappingAttribute
|
|
PAGE_READWRITE | SEC_RESERVE, // flProtect
|
|
size >> 32, // dwMaximumSizeHigh
|
|
size & 0xFFFFFFFF, // dwMaximumSizeLow
|
|
nullptr // lpName
|
|
);
|
|
|
|
// Caller responsible for error handling
|
|
return res;
|
|
}
|
|
|
|
bool XMapper::commit_paging_file_mapping(HANDLE file_handle, uintptr_t file_offset, size_t size) {
|
|
const uintptr_t addr = map_view_no_placeholder(file_handle, file_offset, size);
|
|
if (addr == 0) {
|
|
log_error(gc)("Failed to map view of paging file mapping (%d)", GetLastError());
|
|
return false;
|
|
}
|
|
|
|
const uintptr_t res = commit(addr, size);
|
|
if (res != addr) {
|
|
log_error(gc)("Failed to commit memory (%d)", GetLastError());
|
|
}
|
|
|
|
unmap_view_no_placeholder(addr, size);
|
|
|
|
return res == addr;
|
|
}
|
|
|
|
uintptr_t XMapper::map_view_no_placeholder(HANDLE file_handle, uintptr_t file_offset, size_t size) {
|
|
void* const res = XSyscall::MapViewOfFile3(
|
|
file_handle, // FileMapping
|
|
GetCurrentProcess(), // ProcessHandle
|
|
nullptr, // BaseAddress
|
|
file_offset, // Offset
|
|
size, // ViewSize
|
|
0, // AllocationType
|
|
PAGE_NOACCESS, // PageProtection
|
|
nullptr, // ExtendedParameters
|
|
0 // ParameterCount
|
|
);
|
|
|
|
// Caller responsible for error handling
|
|
return (uintptr_t)res;
|
|
}
|
|
|
|
void XMapper::unmap_view_no_placeholder(uintptr_t addr, size_t size) {
|
|
const bool res = XSyscall::UnmapViewOfFile2(
|
|
GetCurrentProcess(), // ProcessHandle
|
|
(void*)addr, // BaseAddress
|
|
0 // UnmapFlags
|
|
);
|
|
|
|
if (!res) {
|
|
fatal_error("Failed to unmap memory", addr, size);
|
|
}
|
|
}
|
|
|
|
uintptr_t XMapper::commit(uintptr_t addr, size_t size) {
|
|
void* const res = XSyscall::VirtualAlloc2(
|
|
GetCurrentProcess(), // Process
|
|
(void*)addr, // BaseAddress
|
|
size, // Size
|
|
MEM_COMMIT, // AllocationType
|
|
PAGE_NOACCESS, // PageProtection
|
|
nullptr, // ExtendedParameters
|
|
0 // ParameterCount
|
|
);
|
|
|
|
// Caller responsible for error handling
|
|
return (uintptr_t)res;
|
|
}
|
|
|
|
HANDLE XMapper::create_and_commit_paging_file_mapping(size_t size) {
|
|
HANDLE const file_handle = create_paging_file_mapping(size);
|
|
if (file_handle == 0) {
|
|
log_error(gc)("Failed to create paging file mapping (%d)", GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
const bool res = commit_paging_file_mapping(file_handle, 0 /* file_offset */, size);
|
|
if (!res) {
|
|
close_paging_file_mapping(file_handle);
|
|
return 0;
|
|
}
|
|
|
|
return file_handle;
|
|
}
|
|
|
|
void XMapper::close_paging_file_mapping(HANDLE file_handle) {
|
|
const bool res = CloseHandle(
|
|
file_handle // hObject
|
|
);
|
|
|
|
if (!res) {
|
|
fatal("Failed to close paging file handle (%d)", GetLastError());
|
|
}
|
|
}
|
|
|
|
HANDLE XMapper::create_shared_awe_section() {
|
|
MEM_EXTENDED_PARAMETER parameter = { 0 };
|
|
parameter.Type = MemSectionExtendedParameterUserPhysicalFlags;
|
|
parameter.ULong64 = 0;
|
|
|
|
HANDLE section = XSyscall::CreateFileMapping2(
|
|
INVALID_HANDLE_VALUE, // File
|
|
nullptr, // SecurityAttributes
|
|
SECTION_MAP_READ | SECTION_MAP_WRITE, // DesiredAccess
|
|
PAGE_READWRITE, // PageProtection
|
|
SEC_RESERVE | SEC_LARGE_PAGES, // AllocationAttributes
|
|
0, // MaximumSize
|
|
nullptr, // Name
|
|
¶meter, // ExtendedParameters
|
|
1 // ParameterCount
|
|
);
|
|
|
|
if (section == nullptr) {
|
|
fatal("Could not create shared AWE section (%d)", GetLastError());
|
|
}
|
|
|
|
return section;
|
|
}
|
|
|
|
uintptr_t XMapper::reserve_for_shared_awe(HANDLE awe_section, uintptr_t addr, size_t size) {
|
|
MEM_EXTENDED_PARAMETER parameter = { 0 };
|
|
parameter.Type = MemExtendedParameterUserPhysicalHandle;
|
|
parameter.Handle = awe_section;
|
|
|
|
void* const res = XSyscall::VirtualAlloc2(
|
|
GetCurrentProcess(), // Process
|
|
(void*)addr, // BaseAddress
|
|
size, // Size
|
|
MEM_RESERVE | MEM_PHYSICAL, // AllocationType
|
|
PAGE_READWRITE, // PageProtection
|
|
¶meter, // ExtendedParameters
|
|
1 // ParameterCount
|
|
);
|
|
|
|
// Caller responsible for error handling
|
|
return (uintptr_t)res;
|
|
}
|
|
|
|
void XMapper::unreserve_for_shared_awe(uintptr_t addr, size_t size) {
|
|
bool res = VirtualFree(
|
|
(void*)addr, // lpAddress
|
|
0, // dwSize
|
|
MEM_RELEASE // dwFreeType
|
|
);
|
|
|
|
if (!res) {
|
|
fatal("Failed to unreserve memory: " PTR_FORMAT " " SIZE_FORMAT "M (%d)",
|
|
addr, size / M, GetLastError());
|
|
}
|
|
}
|
|
|
|
void XMapper::split_placeholder(uintptr_t addr, size_t size) {
|
|
const bool res = VirtualFree(
|
|
(void*)addr, // lpAddress
|
|
size, // dwSize
|
|
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER // dwFreeType
|
|
);
|
|
|
|
if (!res) {
|
|
fatal_error("Failed to split placeholder", addr, size);
|
|
}
|
|
}
|
|
|
|
void XMapper::coalesce_placeholders(uintptr_t addr, size_t size) {
|
|
const bool res = VirtualFree(
|
|
(void*)addr, // lpAddress
|
|
size, // dwSize
|
|
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS // dwFreeType
|
|
);
|
|
|
|
if (!res) {
|
|
fatal_error("Failed to coalesce placeholders", addr, size);
|
|
}
|
|
}
|
|
|
|
void XMapper::map_view_replace_placeholder(HANDLE file_handle, uintptr_t file_offset, uintptr_t addr, size_t size) {
|
|
void* const res = XSyscall::MapViewOfFile3(
|
|
file_handle, // FileMapping
|
|
GetCurrentProcess(), // ProcessHandle
|
|
(void*)addr, // BaseAddress
|
|
file_offset, // Offset
|
|
size, // ViewSize
|
|
MEM_REPLACE_PLACEHOLDER, // AllocationType
|
|
PAGE_READWRITE, // PageProtection
|
|
nullptr, // ExtendedParameters
|
|
0 // ParameterCount
|
|
);
|
|
|
|
if (res == nullptr) {
|
|
fatal_error("Failed to map memory", addr, size);
|
|
}
|
|
}
|
|
|
|
void XMapper::unmap_view_preserve_placeholder(uintptr_t addr, size_t size) {
|
|
const bool res = XSyscall::UnmapViewOfFile2(
|
|
GetCurrentProcess(), // ProcessHandle
|
|
(void*)addr, // BaseAddress
|
|
MEM_PRESERVE_PLACEHOLDER // UnmapFlags
|
|
);
|
|
|
|
if (!res) {
|
|
fatal_error("Failed to unmap memory", addr, size);
|
|
}
|
|
}
|