diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 935262ee22..47adddd5f9 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -2168,28 +2168,28 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8* { case 1: { - vm::range_lock(range_lock, eal, 1); + vm::range_lock<1>(range_lock, eal, 1); *reinterpret_cast(dst) = *reinterpret_cast(src); range_lock->release(0); break; } case 2: { - vm::range_lock(range_lock, eal, 2); + vm::range_lock<2>(range_lock, eal, 2); *reinterpret_cast(dst) = *reinterpret_cast(src); range_lock->release(0); break; } case 4: { - vm::range_lock(range_lock, eal, 4); + vm::range_lock<4>(range_lock, eal, 4); *reinterpret_cast(dst) = *reinterpret_cast(src); range_lock->release(0); break; } case 8: { - vm::range_lock(range_lock, eal, 8); + vm::range_lock<8>(range_lock, eal, 8); *reinterpret_cast(dst) = *reinterpret_cast(src); range_lock->release(0); break; @@ -3223,9 +3223,73 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) } // Ensure data is allocated (HACK: would raise LR event if not) - vm::range_lock(range_lock, addr, 128); + // Set range_lock first optimistically + range_lock->store(u64{128} << 32 | addr); - const bool res = *range_lock && cmp_rdata(data, vm::_ref(addr)); + u64 lock_val = vm::g_range_lock; + u64 old_lock = 0; + + while (lock_val != old_lock) + { + // Since we want to read data, let's check readability first + if (!(lock_val & vm::range_readable)) + { + // Only one abnormal operation is "unreadable" + if ((lock_val >> vm::range_pos) == (vm::range_locked >> vm::range_pos)) + { + // All page flags are untouched and can be read safely + if (!vm::check_addr(addr, 128)) + { + // Assume our memory is being (de)allocated + range_lock->release(0); + break; + } + + // g_shmem values are unchanged too + const u64 is_shmem = vm::g_shmem[addr >> 16]; + + const u64 test_addr = is_shmem ? (is_shmem | static_cast(addr)) / 128 : u64{addr} / 128; + const u64 lock_addr = lock_val / 128; + + if (test_addr == lock_addr) + { + // Our reservation is locked + range_lock->release(0); + break; + } + + break; + } + } + + // Fallback to normal range check + const u64 lock_addr = static_cast(lock_val); + const u32 lock_size = static_cast(lock_val << 3 >> 35); + + if (lock_addr + lock_size <= addr || lock_addr >= addr + 128) + { + // We are outside locked range, so page flags are unaffected + if (!vm::check_addr(addr, 128)) + { + range_lock->release(0); + break; + } + } + else if (!(lock_val & vm::range_readable)) + { + range_lock->release(0); + break; + } + + old_lock = std::exchange(lock_val, vm::g_range_lock); + } + + if (!range_lock->load()) [[unlikely]] + { + return true; + } + + const bool res = cmp_rdata(data, vm::_ref(addr)); range_lock->release(0); return !res; diff --git a/rpcs3/Emu/Memory/vm_locking.h b/rpcs3/Emu/Memory/vm_locking.h index ca52734b26..fce204fcc4 100644 --- a/rpcs3/Emu/Memory/vm_locking.h +++ b/rpcs3/Emu/Memory/vm_locking.h @@ -42,7 +42,7 @@ namespace vm void range_lock_internal(atomic_t* range_lock, u32 begin, u32 size); // Lock memory range - template + template FORCE_INLINE void range_lock(atomic_t* range_lock, u32 begin, u32 _size) { const u32 size = Size ? Size : _size; @@ -63,7 +63,7 @@ namespace vm { lock_size = 128; - if (TouchMem && is_share) [[unlikely]] + if (is_share) [[unlikely]] { addr = static_cast(begin) | is_share; lock_addr = lock_val; @@ -80,30 +80,10 @@ namespace vm if (!new_lock_val || new_lock_val == lock_val) [[likely]] { - if constexpr (!TouchMem) - { - if (!vm::check_addr(begin, size, vm::page_writable)) - { - range_lock->release(0); - } - } - return; } range_lock->release(0); - - if constexpr (!TouchMem) - { - return; - } - } - - if constexpr (!TouchMem) - { - // Give up - range_lock->release(0); - return; } // Fallback to slow path