[SPU, TSX] Fix reservation corruption in PUTLLC

Change reservation locking logic.
This commit is contained in:
Nekotekina 2019-10-11 16:57:20 +03:00
parent 3ad743ecaa
commit 49e96b39dd
2 changed files with 32 additions and 31 deletions

View file

@ -1137,14 +1137,9 @@ extern bool ppu_stwcx(ppu_thread& ppu, u32 addr, u32 reg_value)
auto& res = vm::reservation_acquire(addr, sizeof(u32));
const auto [_, ok] = res.fetch_op([&](u64& reserv)
{
return (++reserv & -128) == ppu.rtime;
});
ppu.raddr = 0;
if (ok)
if (res == ppu.rtime && res.compare_and_swap_test(ppu.rtime, ppu.rtime | 1))
{
if (data.compare_and_swap_test(old_data, reg_value))
{
@ -1258,14 +1253,9 @@ extern bool ppu_stdcx(ppu_thread& ppu, u32 addr, u64 reg_value)
auto& res = vm::reservation_acquire(addr, sizeof(u64));
const auto [_, ok] = res.fetch_op([&](u64& reserv)
{
return (++reserv & -128) == ppu.rtime;
});
ppu.raddr = 0;
if (ok)
if (res == ppu.rtime && res.compare_and_swap_test(ppu.rtime, ppu.rtime | 1))
{
if (data.compare_and_swap_test(old_data, reg_value))
{

View file

@ -349,7 +349,6 @@ const auto spu_putllc_tx = build_function_asm<u32(*)(u32 raddr, u64 rtime, const
c.bind(fall);
c.sar(x86::eax, 24);
c.js(fail);
c.lock().add(x86::qword_ptr(x86::rbx), 1);
c.lock().bts(x86::dword_ptr(args[2], ::offset32(&spu_thread::state) - ::offset32(&spu_thread::rdata)), static_cast<u32>(cpu_flag::wait));
// Touch memory if transaction failed without RETRY flag on the first attempt
@ -364,6 +363,13 @@ const auto spu_putllc_tx = build_function_asm<u32(*)(u32 raddr, u64 rtime, const
// Lightened transaction: only compare and swap data
c.bind(next);
// Try to "lock" reservation
c.mov(x86::rax, x86::r13);
c.add(x86::r13, 1);
c.lock().cmpxchg(x86::qword_ptr(x86::rbx), x86::r13);
c.jne(fail);
build_transaction_enter(c, fall2, x86::r12, 666);
if (s_tsx_avx)
@ -848,7 +854,6 @@ const auto spu_putlluc_tx = build_function_asm<u32(*)(u32 raddr, const void* rda
//c.jmp(fall);
c.bind(fall);
c.lock().add(x86::qword_ptr(x86::rbx), 1);
c.lock().bts(x86::dword_ptr(args[2], ::offset32(&spu_thread::state)), static_cast<u32>(cpu_flag::wait));
// Touch memory if transaction failed without RETRY flag on the first attempt
@ -859,9 +864,15 @@ const auto spu_putlluc_tx = build_function_asm<u32(*)(u32 raddr, const void* rda
c.xor_(x86::rbp, 0xf80);
Label fall2 = c.newLabel();
Label fail2 = c.newLabel();
// Lightened transaction
c.bind(next);
// Try to acquire "PUTLLUC lock"
c.lock().bts(x86::qword_ptr(x86::rbx), 6);
c.jc(fail2);
build_transaction_enter(c, fall2, x86::r12, 666);
if (s_tsx_avx)
@ -884,10 +895,14 @@ const auto spu_putlluc_tx = build_function_asm<u32(*)(u32 raddr, const void* rda
}
c.xend();
c.lock().add(x86::qword_ptr(x86::rbx), 127);
c.lock().add(x86::qword_ptr(x86::rbx), 64);
c.mov(x86::eax, 1);
c.jmp(_ret);
c.bind(fail2);
c.xor_(x86::eax, x86::eax);
c.jmp(_ret);
c.bind(fall2);
c.mov(x86::eax, 2);
//c.jmp(_ret);
@ -1617,24 +1632,16 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args)
{
cpu_thread::suspend_all cpu_lock(this);
// Try to obtain bit 7 (+64)
if (!vm::reservation_acquire(addr, 128).bts(6))
if (vm::reservation_acquire(addr, 128) & 64)
{
auto& data = vm::_ref<decltype(rdata)>(addr);
mov_rdata(data, to_write);
// Keep checking written data against a rogue transaction sneak in
while (std::atomic_thread_fence(std::memory_order_seq_cst), !cmp_rdata(data, to_write))
// Wait for PUTLLC to complete
while (vm::reservation_acquire(addr, 128) & 1)
{
mov_rdata(data, to_write);
busy_wait(100);
}
vm::reservation_acquire(addr, 128) += 63;
}
else
{
// Give up if another PUTLLUC command took precedence
vm::reservation_acquire(addr, 128) -= 1;
mov_rdata(vm::_ref<decltype(rdata)>(addr), to_write);
vm::reservation_acquire(addr, 128) += 64;
}
}
}
@ -1921,8 +1928,8 @@ bool spu_thread::process_mfc_cmd()
cpu_thread::suspend_all cpu_lock(this);
// Give up if other PUTLLC/PUTLLUC commands are in progress
if (!vm::reservation_acquire(addr, 128).try_dec(rtime + 1))
// Give up if PUTLLUC happened
if (vm::reservation_acquire(addr, 128) == (rtime | 1))
{
auto& data = vm::_ref<decltype(rdata)>(addr);
@ -1937,6 +1944,10 @@ bool spu_thread::process_mfc_cmd()
vm::reservation_acquire(addr, 128) -= 1;
}
}
else
{
vm::reservation_acquire(addr, 128) -= 1;
}
}
}
else if (auto& data = vm::_ref<decltype(rdata)>(addr); rtime == (vm::reservation_acquire(raddr, 128) & -128) && cmp_rdata(rdata, data))