SPU: improve spu_thread::reservation_check

Use optimistic locking and optimistic loop (expecting 1 iteration).
This commit is contained in:
Nekotekina 2020-11-08 08:01:35 +03:00
parent 21ec32b465
commit 3507cd0a37
2 changed files with 72 additions and 28 deletions

View File

@ -2168,28 +2168,28 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8*
{
case 1:
{
vm::range_lock<true, 1>(range_lock, eal, 1);
vm::range_lock<1>(range_lock, eal, 1);
*reinterpret_cast<u8*>(dst) = *reinterpret_cast<const u8*>(src);
range_lock->release(0);
break;
}
case 2:
{
vm::range_lock<true, 2>(range_lock, eal, 2);
vm::range_lock<2>(range_lock, eal, 2);
*reinterpret_cast<u16*>(dst) = *reinterpret_cast<const u16*>(src);
range_lock->release(0);
break;
}
case 4:
{
vm::range_lock<true, 4>(range_lock, eal, 4);
vm::range_lock<4>(range_lock, eal, 4);
*reinterpret_cast<u32*>(dst) = *reinterpret_cast<const u32*>(src);
range_lock->release(0);
break;
}
case 8:
{
vm::range_lock<true, 8>(range_lock, eal, 8);
vm::range_lock<8>(range_lock, eal, 8);
*reinterpret_cast<u64*>(dst) = *reinterpret_cast<const u64*>(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<false>(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<decltype(rdata)>(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<u16>(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<u32>(lock_val);
const u32 lock_size = static_cast<u32>(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<decltype(rdata)>(addr));
range_lock->release(0);
return !res;

View File

@ -42,7 +42,7 @@ namespace vm
void range_lock_internal(atomic_t<u64, 64>* range_lock, u32 begin, u32 size);
// Lock memory range
template <bool TouchMem = true, uint Size = 0>
template <uint Size = 0>
FORCE_INLINE void range_lock(atomic_t<u64, 64>* 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<u16>(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