mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-04 13:07:32 +00:00
fix(agc): expire collected write proofs on allocation changes
This commit is contained in:
1 parent
b8f6d316a9
commit
c19dcecfec
3 files changed
+59
-8
No files matched your search
@@ -1383,6 +1383,14 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(agc_collect_mapping_history_ring PROPERTIES ENVIRONMENT "APS5_NO_EPOCH_COLLECT_MAP=1")
|
||||
set_tests_properties(agc_collect_mapping_history_shared PROPERTIES ENVIRONMENT "APS5_SHARED_COLLECT_MEMO=1")
|
||||
set_tests_properties(agc_collect_mapping_history_uncached PROPERTIES ENVIRONMENT "APS5_NO_COLLECT_MEMO=1")
|
||||
if(WIN32)
|
||||
foreach(mode IN ITEMS cached uncached)
|
||||
add_test(NAME agc_hint_mapping_history_${mode} COMMAND agc_driver_recorder_tests --hint-mapping-history-only)
|
||||
set_tests_properties(agc_hint_mapping_history_${mode} PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30
|
||||
ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endforeach()
|
||||
set_tests_properties(agc_hint_mapping_history_uncached PROPERTIES ENVIRONMENT "APS5_NO_COLLECT_MEMO=1")
|
||||
endif()
|
||||
add_test(NAME agc_storage_kept_unit_settle COMMAND agc_driver_recorder_tests --kept-unit-settle-only)
|
||||
set_tests_properties(agc_storage_kept_unit_settle PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
|
||||
if(WIN32)
|
||||
|
||||
@@ -873,6 +873,7 @@ struct WriteTracker {
|
||||
std::uintptr_t end = 0;
|
||||
std::uint64_t epoch = 0;
|
||||
std::uint64_t unwatched = 0;
|
||||
std::uint64_t mapping = 0;
|
||||
};
|
||||
std::array<Memo, 256> memo{};
|
||||
std::size_t nextMemo = 0;
|
||||
@@ -1347,19 +1348,20 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi
|
||||
// must not differ between them.
|
||||
const auto epoch = currentCollectEpoch();
|
||||
const auto unwatched = unwatchSerial.load(std::memory_order_acquire);
|
||||
const auto mapping = GuestAllocations::GuestAllocationsGeneration_nid_postfix();
|
||||
const bool useMemo = memoized && collectMemoEnabled() && bytes != 0;
|
||||
if (useMemo && !sharedCollectMemo()) {
|
||||
// An entry exists only for a completed walk of an in-arena range, so the tracker is
|
||||
// initialized and watched; nothing below the lock needs asking.
|
||||
if (epochCollectMapEnabled()) {
|
||||
const auto& map = epochCollectMap(epoch);
|
||||
if (const auto found = map.ranges.find(first); found != map.ranges.end() && found->second.unwatched == unwatched && stop <= found->second.end) {
|
||||
if (const auto found = map.ranges.find(first); found != map.ranges.end() && found->second.unwatched == unwatched && found->second.mapping == mapping && stop <= found->second.end) {
|
||||
collectMemoHits.fetch_add(1, std::memory_order_relaxed);
|
||||
return tracker.generation.load(std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
for (const auto& entry : threadCollectMemo.entries) {
|
||||
if (entry.epoch == epoch && entry.unwatched == unwatched && entry.begin <= first && stop <= entry.end) {
|
||||
if (entry.epoch == epoch && entry.unwatched == unwatched && entry.mapping == mapping && entry.begin <= first && stop <= entry.end) {
|
||||
// The current generation, not the memoized one: blocks stamped since (MarkWritten, an
|
||||
// overlapping collect) were written before this caller reads, and an older value would
|
||||
// make every later UnchangedSince fail until the epoch ends.
|
||||
@@ -1374,7 +1376,7 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi
|
||||
auto cursor = first;
|
||||
if (useMemo && sharedCollectMemo()) {
|
||||
for (const auto& entry : tracker.memo) {
|
||||
if (entry.epoch == epoch && entry.unwatched == unwatchSerial.load(std::memory_order_relaxed) && entry.begin <= cursor && stop <= entry.end) {
|
||||
if (entry.epoch == epoch && entry.unwatched == unwatchSerial.load(std::memory_order_relaxed) && entry.mapping == mapping && entry.begin <= cursor && stop <= entry.end) {
|
||||
collectMemoHits.fetch_add(1, std::memory_order_relaxed);
|
||||
return tracker.generation;
|
||||
}
|
||||
@@ -1395,12 +1397,12 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi
|
||||
// Only a completed walk is remembered; a failed one (uncommitted pages) returned 0 above.
|
||||
if (collectMemoEnabled()) {
|
||||
const auto serial = unwatchSerial.load(std::memory_order_relaxed);
|
||||
if (sharedCollectMemo()) tracker.memo[tracker.nextMemo++ % tracker.memo.size()] = {first, stop, epoch, serial};
|
||||
if (sharedCollectMemo()) tracker.memo[tracker.nextMemo++ % tracker.memo.size()] = {first, stop, epoch, serial, mapping};
|
||||
else {
|
||||
threadCollectMemo.entries[threadCollectMemo.next++ % threadCollectMemo.entries.size()] = {first, stop, epoch, serial};
|
||||
threadCollectMemo.entries[threadCollectMemo.next++ % threadCollectMemo.entries.size()] = {first, stop, epoch, serial, mapping};
|
||||
if (epochCollectMapEnabled()) {
|
||||
auto& entry = epochCollectMap(epoch).ranges[first];
|
||||
if (entry.unwatched != serial || entry.end < stop) entry = {first, stop, epoch, serial};
|
||||
if (entry.unwatched != serial || entry.mapping != mapping || entry.end < stop) entry = {first, stop, epoch, serial, mapping};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1826,15 +1828,16 @@ std::uint64_t UntrackedHint(std::uint64_t address, std::size_t bytes) {
|
||||
tracker.initialize();
|
||||
if (!tracker.watched || !tracker.hinted(first, stop)) return 0;
|
||||
const auto unwatched = unwatchSerial.load(std::memory_order_relaxed);
|
||||
const auto mapping = GuestAllocations::GuestAllocationsGeneration_nid_postfix();
|
||||
const bool memoized = collectMemoEnabled() && std::any_of(threadHintMemo.entries.begin(), threadHintMemo.entries.end(), [&](const WriteTracker::Memo& entry) {
|
||||
return entry.epoch == epoch && entry.unwatched == unwatched && entry.begin <= first && stop <= entry.end;
|
||||
return entry.epoch == epoch && entry.unwatched == unwatched && entry.mapping == mapping && entry.begin <= first && stop <= entry.end;
|
||||
});
|
||||
if (memoized) {
|
||||
collectMemoHits.fetch_add(1, std::memory_order_relaxed);
|
||||
} else {
|
||||
const TimedAccess timed(CounterCollect, bytes);
|
||||
if (!walkHints(tracker, first, stop)) return 0;
|
||||
if (collectMemoEnabled()) threadHintMemo.entries[threadHintMemo.next++ % threadHintMemo.entries.size()] = {first, stop, epoch, unwatched};
|
||||
if (collectMemoEnabled()) threadHintMemo.entries[threadHintMemo.next++ % threadHintMemo.entries.size()] = {first, stop, epoch, unwatched, mapping};
|
||||
}
|
||||
return tracker.newestHint(first, stop);
|
||||
#else
|
||||
|
||||
@@ -169,6 +169,41 @@ std::optional<std::size_t> selectDevice(std::span<const VkPhysicalDeviceProperti
|
||||
return selected;
|
||||
}
|
||||
|
||||
bool hintMappingHistoryTests() {
|
||||
#ifdef _WIN32
|
||||
constexpr std::size_t bytes = 131072;
|
||||
auto* memory = static_cast<std::uint8_t*>(AllocateWatched(bytes, 65536));
|
||||
if (memory == nullptr) return false;
|
||||
GuestAllocations::Mutation().Add(memory, bytes, true, true, false);
|
||||
struct Cleanup {
|
||||
std::uint8_t* memory;
|
||||
~Cleanup() {
|
||||
GuestAllocations::Mutation().Remove(memory);
|
||||
ReleaseWatched(memory, bytes);
|
||||
}
|
||||
} cleanup{memory};
|
||||
const auto address = reinterpret_cast<std::uint64_t>(memory);
|
||||
std::memset(memory, 0x11, bytes);
|
||||
AgcDriver::GuestMemory::BumpCollectEpoch();
|
||||
AgcDriver::GuestMemory::Unwatch(address, bytes);
|
||||
const auto before = AgcDriver::GuestMemory::UntrackedHint(address, bytes);
|
||||
Require(before != 0, "the hint mapping fixture has no write hint");
|
||||
Require(AgcDriver::GuestMemory::UntrackedHint(address, bytes) == before, "an unchanged hint range advanced its history");
|
||||
memory[4096] = 0x22;
|
||||
GuestAllocations::Mutation().Remove(memory);
|
||||
GuestAllocations::Mutation().Add(memory, bytes, true, true, false);
|
||||
const auto after = AgcDriver::GuestMemory::UntrackedHint(address, bytes);
|
||||
Require(after > before, "a cached hint walk hid a CPU store across allocation replacement");
|
||||
Require(memory[4096] == 0x22 && memory[bytes - 4096] == 0x11, "collecting replacement hints changed guest bytes");
|
||||
GuestAllocations::Mutation().Remove(memory);
|
||||
GuestAllocations::Mutation().Add(memory, bytes, true, true, false);
|
||||
Require(AgcDriver::GuestMemory::UntrackedHint(address, bytes) == after, "allocation replacement invented a CPU write hint");
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void deviceSelectionTests() {
|
||||
const auto properties = [](const char* name, VkPhysicalDeviceType type) {
|
||||
VkPhysicalDeviceProperties result{};
|
||||
@@ -9672,6 +9707,11 @@ int main(int argc, char** argv) {
|
||||
std::cout << "collect mapping history tests passed\n";
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::string_view(argv[1]) == "--hint-mapping-history-only") {
|
||||
if (!hintMappingHistoryTests()) return 77;
|
||||
std::cout << "hint mapping history tests passed\n";
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::string_view(argv[1]) == "--device-selection-only") {
|
||||
deviceSelectionTests();
|
||||
std::cout << "recorder device selection tests passed\n";
|
||||
|
||||
Reference in new issue
Block a user