blob: 48a32157f5963175866511b929d1e4cbc7d97c14 [file]
/*
* Copyright (c) 2019, 2025, 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 "gc/z/zAddress.inline.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zLargePages.inline.hpp"
#include "gc/z/zMapper_windows.hpp"
#include "gc/z/zSyscall_windows.hpp"
#include "gc/z/zValue.inline.hpp"
#include "gc/z/zVirtualMemory.inline.hpp"
#include "gc/z/zVirtualMemoryManager.inline.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
class ZVirtualMemoryReserverImpl : public CHeapObj<mtGC> {
public:
virtual void register_callbacks(ZVirtualMemoryRegistry* registry) {}
virtual bool reserve(zaddress_unsafe addr, size_t size) = 0;
virtual void unreserve(zaddress_unsafe addr, size_t size) = 0;
};
// Implements small pages (paged) support using placeholder reservation.
//
// When a memory area is available (kept by the virtual memory manager) a
// single placeholder is covering that memory area. When memory is
// removed from the registry the placeholder is split into granule
// sized placeholders to allow mapping operations on that granularity.
class ZVirtualMemoryReserverSmallPages : public ZVirtualMemoryReserverImpl {
private:
class PlaceholderCallbacks : public AllStatic {
private:
static void split_placeholder(zoffset start, size_t size) {
ZMapper::split_placeholder(ZOffset::address_unsafe(start), size);
}
static void coalesce_placeholders(zoffset start, size_t size) {
ZMapper::coalesce_placeholders(ZOffset::address_unsafe(start), size);
}
// Turn the single placeholder covering the memory area into granule
// sized placeholders.
static void split_into_granule_sized_placeholders(zoffset start, size_t size) {
assert(size >= ZGranuleSize, "Must be at least one granule");
assert(is_aligned(size, ZGranuleSize), "Must be granule aligned");
// Don't call split_placeholder on the last granule, since it is already
// a placeholder and the system call would therefore fail.
const size_t limit = size - ZGranuleSize;
for (size_t offset = 0; offset < limit; offset += ZGranuleSize) {
split_placeholder(start + offset, ZGranuleSize);
}
}
static void coalesce_into_one_placeholder(zoffset start, size_t size) {
assert(is_aligned(size, ZGranuleSize), "Must be granule aligned");
// Granule sized areas are already covered by a single placeholder
if (size > ZGranuleSize) {
coalesce_placeholders(start, size);
}
}
// Callback implementations
// Called when a memory area is going to be handed out to be used.
//
// Splits the memory area into granule-sized placeholders.
static void prepare_for_hand_out_callback(const ZVirtualMemory& area) {
assert(is_aligned(area.size(), ZGranuleSize), "Must be granule aligned");
split_into_granule_sized_placeholders(area.start(), area.size());
}
// Called when a memory area is handed back to the memory manager.
//
// Combines the granule-sized placeholders into one placeholder.
static void prepare_for_hand_back_callback(const ZVirtualMemory& area) {
assert(is_aligned(area.size(), ZGranuleSize), "Must be granule aligned");
coalesce_into_one_placeholder(area.start(), area.size());
}
// Called when inserting a memory area and it can be merged with an
// existing, adjacent memory area.
//
// Coalesces the underlying placeholders into one.
static void grow_callback(const ZVirtualMemory& from, const ZVirtualMemory& to) {
assert(is_aligned(from.size(), ZGranuleSize), "Must be granule aligned");
assert(is_aligned(to.size(), ZGranuleSize), "Must be granule aligned");
assert(from != to, "Must have grown");
assert(to.contains(from), "Must be within");
coalesce_into_one_placeholder(to.start(), to.size());
}
// Called when a memory area is removed from the front or back of an existing
// memory area.
//
// Splits the memory into two placeholders.
static void shrink_callback(const ZVirtualMemory& from, const ZVirtualMemory& to) {
assert(is_aligned(from.size(), ZGranuleSize), "Must be granule aligned");
assert(is_aligned(to.size(), ZGranuleSize), "Must be granule aligned");
assert(from != to, "Must have shrunk");
assert(from.contains(to), "Must be larger than what we try to split out");
assert(from.start() == to.start() || from.end() == to.end(),
"Only verified to work if we split a placeholder into two placeholders");
// Split the area into two placeholders
split_placeholder(to.start(), to.size());
}
public:
static ZVirtualMemoryRegistry::Callbacks callbacks() {
// Each reserved virtual memory address area registered in _manager is
// exactly covered by a single placeholder. Callbacks are installed so
// that whenever a memory area changes, the corresponding placeholder
// is adjusted.
//
// The prepare_for_hand_out callback is called when virtual memory is
// handed out to callers. The memory area is split into granule-sized
// placeholders.
//
// The prepare_for_hand_back callback is called when previously handed
// out virtual memory is handed back to the memory manager. The
// returned memory area is then covered by a new single placeholder.
//
// The grow callback is called when a virtual memory area grows. The
// resulting memory area is then covered by a single placeholder.
//
// The shrink callback is called when a virtual memory area is split into
// two parts. The two resulting memory areas are then covered by two
// separate placeholders.
//
// See comment in zMapper_windows.cpp explaining why placeholders are
// split into ZGranuleSize sized placeholders.
ZVirtualMemoryRegistry::Callbacks callbacks;
callbacks._prepare_for_hand_out = &prepare_for_hand_out_callback;
callbacks._prepare_for_hand_back = &prepare_for_hand_back_callback;
callbacks._grow = &grow_callback;
callbacks._shrink = &shrink_callback;
return callbacks;
}
};
virtual void register_callbacks(ZVirtualMemoryRegistry* registry) {
registry->register_callbacks(PlaceholderCallbacks::callbacks());
}
virtual bool reserve(zaddress_unsafe addr, size_t size) {
const zaddress_unsafe res = ZMapper::reserve(addr, size);
assert(res == addr || untype(res) == 0, "Should not reserve other memory than requested");
return res == addr;
}
virtual void unreserve(zaddress_unsafe addr, size_t size) {
ZMapper::unreserve(addr, size);
}
};
// Implements Large Pages (locked) support using shared AWE physical memory.
// ZPhysicalMemory layer needs access to the section
HANDLE ZAWESection;
class ZVirtualMemoryReserverLargePages : public ZVirtualMemoryReserverImpl {
private:
virtual bool reserve(zaddress_unsafe addr, size_t size) {
const zaddress_unsafe res = ZMapper::reserve_for_shared_awe(ZAWESection, addr, size);
assert(res == addr || untype(res) == 0, "Should not reserve other memory than requested");
return res == addr;
}
virtual void unreserve(zaddress_unsafe addr, size_t size) {
ZMapper::unreserve_for_shared_awe(addr, size);
}
public:
ZVirtualMemoryReserverLargePages() {
ZAWESection = ZMapper::create_shared_awe_section();
}
};
static ZVirtualMemoryReserverImpl* _impl = nullptr;
void ZVirtualMemoryReserverImpl_initialize() {
assert(_impl == nullptr, "Should only initialize once");
if (ZLargePages::is_enabled()) {
_impl = new ZVirtualMemoryReserverLargePages();
} else {
_impl = new ZVirtualMemoryReserverSmallPages();
}
}
void ZVirtualMemoryReserver::pd_register_callbacks(ZVirtualMemoryRegistry* registry) {
_impl->register_callbacks(registry);
}
bool ZVirtualMemoryReserver::pd_reserve(zaddress_unsafe addr, size_t size) {
return _impl->reserve(addr, size);
}
void ZVirtualMemoryReserver::pd_unreserve(zaddress_unsafe addr, size_t size) {
_impl->unreserve(addr, size);
}