| /* | |
| * 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); | |
| } |