blob: d8753dc22833c23e6a4e5bc4c3703f9abced2ae7 [file] [log] [blame]
/*
* Copyright (c) 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/z/zAddress.inline.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zNMT.hpp"
#include "gc/z/zVirtualMemory.hpp"
#include "memory/allocation.hpp"
#include "services/memTracker.hpp"
#include "utilities/nativeCallStack.hpp"
ZNMT::Reservation ZNMT::_reservations[ZMaxVirtualReservations] = {};
size_t ZNMT::_num_reservations = 0;
size_t ZNMT::reservation_index(zoffset offset, size_t* offset_in_reservation) {
assert(_num_reservations > 0, "at least one reservation must exist");
size_t index = 0;
*offset_in_reservation = untype(offset);
for (; index < _num_reservations; ++index) {
const size_t reservation_size = _reservations[index]._size;
if (*offset_in_reservation < reservation_size) {
break;
}
*offset_in_reservation -= reservation_size;
}
assert(index != _num_reservations, "failed to find reservation index");
return index;
}
void ZNMT::process_fake_mapping(zoffset offset, size_t size, bool commit) {
// In order to satisfy NTM's requirement of an 1:1 mapping between committed
// and reserved addresses, a fake mapping from the offset into the reservation
// is used.
//
// These mappings from
// [offset, offset + size) -> {[virtual address range], ...}
// are stable after the heap has been reserved. No commits proceed any
// reservations. Committing and uncommitting the same [offset, offset + size)
// range will result in same virtual memory ranges.
size_t left_to_process = size;
size_t offset_in_reservation;
for (size_t i = reservation_index(offset, &offset_in_reservation); i < _num_reservations; ++i) {
const zaddress_unsafe reservation_start = _reservations[i]._start;
const size_t reservation_size = _reservations[i]._size;
const size_t sub_range_size = MIN2(left_to_process, reservation_size - offset_in_reservation);
const uintptr_t sub_range_addr = untype(reservation_start) + offset_in_reservation;
// commit / uncommit memory
if (commit) {
MemTracker::record_virtual_memory_commit((void*)sub_range_addr, sub_range_size, CALLER_PC);
} else {
if (MemTracker::enabled()) {
Tracker tracker(Tracker::uncommit);
tracker.record((address)sub_range_addr, sub_range_size);
}
}
left_to_process -= sub_range_size;
if (left_to_process == 0) {
// Processed all nmt registrations
return;
}
offset_in_reservation = 0;
}
assert(left_to_process == 0, "everything was not commited");
}
void ZNMT::reserve(zaddress_unsafe start, size_t size) {
assert(_num_reservations < ZMaxVirtualReservations, "too many reservations");
// Keep track of the reservations made in order to create fake mappings
// between the reserved and commited memory.
// See details in ZNMT::process_fake_mapping
_reservations[_num_reservations++] = {start, size};
MemTracker::record_virtual_memory_reserve((void*)untype(start), size, CALLER_PC, mtJavaHeap);
}
void ZNMT::commit(zoffset offset, size_t size) {
// NMT expects a 1-to-1 mapping between virtual and physical memory.
// ZGC can temporarily have multiple virtual addresses pointing to
// the same physical memory.
//
// When this function is called we don't know where in the virtual memory
// this physical memory will be mapped. So we fake the virtual memory
// address by mapping the physical offset into offsets in the reserved
// memory space.
process_fake_mapping(offset, size, true);
}
void ZNMT::uncommit(zoffset offset, size_t size) {
// We fake the virtual memory address by mapping the physical offset
// into offsets in the reserved memory space.
// See comment in ZNMT::commit
process_fake_mapping(offset, size, false);
}