blob: cd0f81536aedad4d4c5d1d2ff184c7d1f8041f4d [file] [log] [blame]
/*
* Copyright (c) 2020-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "pas_config.h"
#if LIBPAS_ENABLED
#include "pas_bitfit_page.h"
#include "pas_bitfit_directory.h"
#include "pas_bitfit_view.h"
#include "pas_epoch.h"
#include "pas_log.h"
void pas_bitfit_page_construct(pas_bitfit_page* page,
pas_bitfit_view* view,
pas_bitfit_page_config* config_ptr)
{
static const bool verbose = false;
pas_bitfit_page_config config;
config = *config_ptr;
PAS_ASSERT(pas_page_kind_get_config_kind(config.base.page_kind)
== pas_page_config_kind_bitfit);
pas_lock_assert_held(&view->ownership_lock);
if (verbose) {
pas_log("Constructing page %p with view %p and config %s\n",
page, view, pas_bitfit_page_config_kind_get_string(config.kind));
}
pas_page_base_construct(&page->base, config.base.page_kind);
page->use_epoch = PAS_EPOCH_INVALID;
page->did_note_max_free = false;
page->num_live_bits = 0;
pas_compact_atomic_bitfit_view_ptr_store(&page->owner, view);
pas_zero_memory(page->bits, pas_bitfit_page_config_num_alloc_bit_bytes(config));
pas_bitvector64_set_range(
(uint64_t*)pas_bitfit_page_free_bits(page),
pas_bitfit_page_offset_to_first_object(config) >> config.base.min_align_shift,
config.base.page_size >> config.base.min_align_shift,
true);
if (PAS_ENABLE_TESTING) {
size_t begin;
size_t end;
size_t offset;
begin = pas_bitfit_page_offset_to_first_object(config);
end = pas_bitfit_page_offset_to_end_of_last_object(config);
for (offset = begin; offset < end; offset += pas_page_base_config_min_align(config.base)) {
PAS_TESTING_ASSERT(pas_bitvector_get(pas_bitfit_page_free_bits(page),
offset >> config.base.min_align_shift));
PAS_TESTING_ASSERT(!pas_bitvector_get(pas_bitfit_page_object_end_bits(page, config),
offset >> config.base.min_align_shift));
}
}
if (config.base.page_size != config.base.granule_size) {
pas_page_granule_use_count* use_counts;
size_t num_granules;
uintptr_t start_of_payload;
uintptr_t end_of_payload;
use_counts = pas_bitfit_page_get_granule_use_counts(page, config);
num_granules = config.base.page_size / config.base.granule_size;
if (verbose) {
pas_log("Constructed page at %p, use_counts at %p up to %p\n",
page, use_counts, use_counts + num_granules);
}
pas_zero_memory(use_counts, num_granules * sizeof(pas_page_granule_use_count));
/* If there are any bytes in the page not made available for allocation then make sure
that the use counts know about it. */
start_of_payload = config.base.page_object_payload_offset;
end_of_payload =
config.base.page_object_payload_offset + config.base.page_object_payload_size;
pas_page_granule_increment_uses_for_range(
use_counts, 0, start_of_payload,
config.base.page_size, config.base.granule_size);
pas_page_granule_increment_uses_for_range(
use_counts, end_of_payload, config.base.page_size,
config.base.page_size, config.base.granule_size);
}
if (verbose) {
pas_log("bits after construction:\n");
pas_bitfit_page_log_bits(page, 0, 0);
}
}
pas_bitfit_page_config* pas_bitfit_page_get_config(pas_bitfit_page* page)
{
return pas_bitfit_page_config_kind_get_config(
pas_compact_bitfit_directory_ptr_load_non_null(
&pas_compact_atomic_bitfit_view_ptr_load_non_null(
&page->owner)->directory)->config_kind);
}
void pas_bitfit_page_log_bits(
pas_bitfit_page* page, uintptr_t mark_begin_offset, uintptr_t mark_end_offset)
{
size_t offset;
pas_bitfit_page_config config;
config = *pas_bitfit_page_get_config(page);
pas_log("free bits: ");
for (offset = 0;
offset < config.base.page_size;
offset += pas_page_base_config_min_align(config.base)) {
pas_log("%d", pas_bitvector_get(pas_bitfit_page_free_bits(page),
offset >> config.base.min_align_shift));
}
pas_log("\n");
pas_log(" end bits: ");
for (offset = 0;
offset < config.base.page_size;
offset += pas_page_base_config_min_align(config.base)) {
pas_log("%d", pas_bitvector_get(pas_bitfit_page_object_end_bits(page, config),
offset >> config.base.min_align_shift));
}
pas_log("\n");
if (mark_begin_offset == mark_end_offset)
return;
PAS_ASSERT(mark_end_offset > mark_begin_offset);
pas_log(" ");
for (offset = 0;
offset < config.base.page_size;
offset += pas_page_base_config_min_align(config.base)) {
if (offset >= mark_begin_offset && offset < mark_end_offset)
pas_log("^");
else
pas_log(" ");
}
pas_log("\n");
}
PAS_NO_RETURN void pas_bitfit_page_deallocation_did_fail(
pas_bitfit_page* page,
pas_bitfit_page_config_kind config_kind,
uintptr_t begin,
uintptr_t offset,
const char* reason)
{
pas_start_crash_logging();
pas_log("Thread %p encountered bitfit alloaction error.\n", pthread_self());
pas_log("Bits for page %p (%s):\n",
page, pas_bitfit_page_config_kind_get_string(config_kind));
pas_bitfit_page_log_bits(page, offset, offset + 1);
pas_deallocation_did_fail(reason, begin);
}
bool pas_bitfit_page_for_each_live_object(
pas_bitfit_page* page,
pas_bitfit_page_for_each_live_object_callback callback,
void* arg)
{
pas_bitfit_page_config* config_ptr;
pas_bitfit_page_config config;
void* boundary;
pas_bitfit_view* view;
size_t begin;
size_t end;
size_t offset;
view = pas_compact_atomic_bitfit_view_ptr_load(&page->owner);
config_ptr = pas_bitfit_page_config_kind_get_config(
pas_compact_bitfit_directory_ptr_load_non_null(
&view->directory)->config_kind);
config = *config_ptr;
boundary = pas_bitfit_page_boundary(page, config);
begin = pas_bitfit_page_offset_to_first_object(config);
end = pas_bitfit_page_offset_to_end_of_last_object(config);
for (offset = begin; offset < end; offset += pas_page_base_config_min_align(config.base)) {
size_t end_offset;
if (pas_bitvector_get(pas_bitfit_page_free_bits(page),
offset >> config.base.min_align_shift))
continue;
for (end_offset = offset;
end_offset < end && !pas_bitvector_get(pas_bitfit_page_object_end_bits(page, config),
end_offset >> config.base.min_align_shift);
end_offset += pas_page_base_config_min_align(config.base));
PAS_ASSERT(end_offset >= offset);
PAS_ASSERT(end_offset < end);
if (!callback((uintptr_t)boundary + offset,
end_offset - offset + pas_page_base_config_min_align(config.base),
arg))
return false;
offset = end_offset;
}
return true;
}
typedef struct {
pas_bitfit_page_config* config;
uintptr_t boundary;
pas_page_granule_use_count my_use_counts[PAS_MAX_GRANULES];
} verify_for_each_object_data;
static bool verify_for_each_object_callback(uintptr_t begin,
size_t size,
void* arg)
{
verify_for_each_object_data* data;
data = arg;
pas_page_granule_increment_uses_for_range(
data->my_use_counts,
begin - data->boundary,
begin - data->boundary + size,
data->config->base.page_size,
data->config->base.granule_size);
return true;
}
void pas_bitfit_page_verify(pas_bitfit_page* page)
{
size_t begin;
size_t end;
size_t offset;
pas_bitfit_page_config* config_ptr;
pas_bitfit_page_config config;
size_t num_live_bits;
verify_for_each_object_data data;
bool for_each_result;
size_t index;
pas_page_granule_use_count* use_counts;
uintptr_t start_of_payload;
uintptr_t end_of_payload;
config_ptr = pas_bitfit_page_get_config(page);
config = *config_ptr;
begin = pas_bitfit_page_offset_to_first_object(config);
end = pas_bitfit_page_offset_to_end_of_last_object(config);
num_live_bits = 0;
for (offset = begin; offset < end; offset += pas_page_base_config_min_align(config.base)) {
if (!pas_bitvector_get(pas_bitfit_page_free_bits(page),
offset >> config.base.min_align_shift))
num_live_bits++;
}
PAS_ASSERT(num_live_bits == page->num_live_bits);
if (config.base.page_size == config.base.granule_size)
return;
PAS_ASSERT(config.base.page_size / config.base.granule_size <= PAS_MAX_GRANULES);
data.config = config_ptr;
data.boundary = (uintptr_t)pas_bitfit_page_boundary(page, config);
pas_zero_memory(data.my_use_counts, PAS_MAX_GRANULES);
start_of_payload = config.base.page_object_payload_offset;
end_of_payload =
config.base.page_object_payload_offset + config.base.page_object_payload_size;
pas_page_granule_increment_uses_for_range(
data.my_use_counts, 0, start_of_payload,
config.base.page_size, config.base.granule_size);
pas_page_granule_increment_uses_for_range(
data.my_use_counts, end_of_payload, config.base.page_size,
config.base.page_size, config.base.granule_size);
for_each_result = pas_bitfit_page_for_each_live_object(
page, verify_for_each_object_callback, &data);
PAS_ASSERT(for_each_result);
use_counts = pas_bitfit_page_get_granule_use_counts(page, config);
for (index = config.base.page_size / config.base.granule_size; index--;) {
if (use_counts[index] == PAS_PAGE_GRANULE_DECOMMITTED)
PAS_ASSERT(!data.my_use_counts[index]);
else
PAS_ASSERT(use_counts[index] == data.my_use_counts[index]);
}
}
#endif /* LIBPAS_ENABLED */