SPU: Restore postponed LR notifications (with tweaks)

This commit is contained in:
Elad
2025-09-05 14:12:17 +03:00
committed by Elad
parent f84df03651
commit 0564842871
7 changed files with 53 additions and 42 deletions
+27 -23
View File
@@ -2326,6 +2326,7 @@ void ppu_thread::cpu_wait(bs_t<cpu_flag> old)
if (u32 addr = res_notify) if (u32 addr = res_notify)
{ {
res_notify = 0; res_notify = 0;
res_notify_postpone_streak = 0;
if (res_notify_time + 128 == (vm::reservation_acquire(addr) & -128)) if (res_notify_time + 128 == (vm::reservation_acquire(addr) & -128))
{ {
@@ -3613,34 +3614,17 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
{ {
extern atomic_t<u32> liblv2_begin, liblv2_end; extern atomic_t<u32> liblv2_begin, liblv2_end;
const u32 notify = ppu.res_notify; u32 notify = ppu.res_notify;
if (notify)
{
if (auto waiter = vm::reservation_notifier_notify(notify, ppu.res_notify_time, true))
{
ppu.state += cpu_flag::wait;
waiter->notify_all();
}
ppu.res_notify = 0;
}
// Avoid notifications from lwmutex or sys_spinlock // Avoid notifications from lwmutex or sys_spinlock
const bool is_liblv2_or_null = (ppu.cia >= liblv2_begin && ppu.cia < liblv2_end); const bool is_liblv2_or_null = (ppu.cia >= liblv2_begin && ppu.cia < liblv2_end);
if (!is_liblv2_or_null && (addr ^ notify) & -128) if (!is_liblv2_or_null)
{ {
// Try to postpone notification to when PPU is asleep or join notifications on the same address // Try to postpone notification to when PPU is asleep or join notifications on the same address
// This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock // This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock
const u32 count = vm::reservation_notifier_count(addr & -128, rtime); const u32 count = vm::reservation_notifier_count(addr, rtime);
if (cpu_flag::wait - ppu.state)
{
ppu.state += cpu_flag::wait;
}
vm::reservation_notifier_notify(addr & -128, rtime);
switch (count) switch (count)
{ {
case 0: case 0:
@@ -3650,12 +3634,19 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
} }
case 1: case 1:
{ {
if (!notify) // Postpone notifications if there is no pending one OR if there is likely a complex operation on reservation going on
// Which consists of multiple used addresses
if (ppu.res_notify_postpone_streak <= 4)
{ {
ppu.res_notify = addr & -128; if (!notify || ((notify & -128) == (addr & -128) && new_data != old_data))
{
ppu.res_notify = addr;
ppu.res_notify_time = rtime; ppu.res_notify_time = rtime;
ppu.res_notify_postpone_streak++;
notify = 0;
break; break;
} }
}
// Notify both // Notify both
[[fallthrough]]; [[fallthrough]];
@@ -3667,12 +3658,24 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
ppu.state += cpu_flag::wait; ppu.state += cpu_flag::wait;
} }
vm::reservation_notifier_notify(addr & -128, rtime); vm::reservation_notifier_notify(addr, rtime);
break; break;
} }
} }
} }
if (notify)
{
if (auto waiter = vm::reservation_notifier_notify(notify, ppu.res_notify_time, true))
{
ppu.state += cpu_flag::wait;
waiter->notify_all();
}
ppu.res_notify = 0;
ppu.res_notify_postpone_streak = 0;
}
static_cast<void>(ppu.test_stopped()); static_cast<void>(ppu.test_stopped());
if (addr == ppu.last_faddr) if (addr == ppu.last_faddr)
@@ -3700,6 +3703,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
} }
ppu.res_notify = 0; ppu.res_notify = 0;
ppu.res_notify_postpone_streak = 0;
} }
ppu.raddr = 0; ppu.raddr = 0;
+1
View File
@@ -267,6 +267,7 @@ public:
u32 res_cached{0}; // Reservation "cached" addresss u32 res_cached{0}; // Reservation "cached" addresss
u32 res_notify{0}; u32 res_notify{0};
u64 res_notify_time{0}; u64 res_notify_time{0};
u32 res_notify_postpone_streak{0};
union ppu_prio_t union ppu_prio_t
{ {
+6 -3
View File
@@ -4200,9 +4200,10 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args)
do_cell_atomic_128_store(addr, _ptr<spu_rdata_t>(args.lsa & 0x3ff80)); do_cell_atomic_128_store(addr, _ptr<spu_rdata_t>(args.lsa & 0x3ff80));
// TODO: Implement properly (notifying previous two times) // Cover all waiters (TODO: Get reservation time atomically)
vm::reservation_notifier_notify(addr, vm::reservation_acquire(addr) - 128); const u64 rtime = vm::reservation_acquire(addr);
vm::reservation_notifier_notify(addr, vm::reservation_acquire(addr) - 256); vm::reservation_notifier_notify(addr, rtime - 128);
vm::reservation_notifier_notify(addr, rtime - 256);
} }
bool spu_thread::do_mfc(bool can_escape, bool must_finish) bool spu_thread::do_mfc(bool can_escape, bool must_finish)
@@ -6132,6 +6133,8 @@ s64 spu_thread::get_ch_value(u32 ch)
eventstat_spin_count = 0; eventstat_spin_count = 0;
} }
} }
lv2_obj::notify_all();
} }
else else
{ {
+11 -9
View File
@@ -1336,13 +1336,16 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout)
if (cpu.get_class() == thread_class::ppu) if (cpu.get_class() == thread_class::ppu)
{ {
if (u32 addr = static_cast<ppu_thread&>(cpu).res_notify) ppu_thread& ppu = static_cast<ppu_thread&>(cpu);
if (u32 addr = ppu.res_notify)
{ {
static_cast<ppu_thread&>(cpu).res_notify = 0; ppu.res_notify = 0;
ppu.res_notify_postpone_streak = 0;
if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t<const void>{}); it != std::end(g_to_notify)) if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t<const void>{}); it != std::end(g_to_notify))
{ {
if ((*it++ = vm::reservation_notifier_notify(addr, static_cast<ppu_thread&>(cpu).res_notify_time, true))) if ((*it++ = vm::reservation_notifier_notify(addr, ppu.res_notify_time, true)))
{ {
if (it < std::end(g_to_notify)) if (it < std::end(g_to_notify))
{ {
@@ -1353,7 +1356,7 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout)
} }
else else
{ {
vm::reservation_notifier_notify(addr, static_cast<ppu_thread&>(cpu).res_notify_time); vm::reservation_notifier_notify(addr, ppu.res_notify_time);
} }
} }
} }
@@ -1389,6 +1392,7 @@ bool lv2_obj::awake(cpu_thread* thread, s32 prio)
if (u32 addr = ppu->res_notify) if (u32 addr = ppu->res_notify)
{ {
ppu->res_notify = 0; ppu->res_notify = 0;
ppu->res_notify_postpone_streak = 0;
if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t<const void>{}); it != std::end(g_to_notify)) if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t<const void>{}); it != std::end(g_to_notify))
{ {
@@ -2258,12 +2262,9 @@ void lv2_obj::notify_all() noexcept
atomic_t<u64, 64>* range_lock = nullptr; atomic_t<u64, 64>* range_lock = nullptr;
u32 current_raddr = 0;
if (cpu->get_class() == thread_class::spu) if (cpu->get_class() == thread_class::spu)
{ {
range_lock = static_cast<spu_thread*>(cpu)->range_lock; range_lock = static_cast<spu_thread*>(cpu)->range_lock;
current_raddr = static_cast<spu_thread*>(cpu)->raddr;
} }
for (usz i = 0, checked = 0; checked < 4 && i < total_waiters; i++) for (usz i = 0, checked = 0; checked < 4 && i < total_waiters; i++)
@@ -2272,7 +2273,7 @@ void lv2_obj::notify_all() noexcept
const u64 value = waiter.load(); const u64 value = waiter.load();
u32 raddr = static_cast<u32>(value) & -128; u32 raddr = static_cast<u32>(value) & -128;
if (vm::check_addr(raddr) && (raddr != current_raddr || (value % 128) > 1)) if (vm::check_addr(raddr))
{ {
if (((raddr >> 28) < 2 || (raddr >> 28) == 0xd)) if (((raddr >> 28) < 2 || (raddr >> 28) == 0xd))
{ {
@@ -2337,8 +2338,9 @@ void lv2_obj::notify_all() noexcept
{ {
if (notifies[i]) if (notifies[i])
{ {
vm::reservation_update(notifies[i]); // Cover all waiters for an address
vm::reservation_notifier_notify(notifies[i], notifies_time[i]); vm::reservation_notifier_notify(notifies[i], notifies_time[i]);
vm::reservation_notifier_notify(notifies[i], notifies_time[i] - 128);
} }
} }
} }
-1
View File
@@ -485,7 +485,6 @@ public:
static void enqueue_on_top(const void* waiter) static void enqueue_on_top(const void* waiter)
{ {
return;
g_to_notify[0] = waiter; g_to_notify[0] = waiter;
g_to_notify[1] = nullptr; g_to_notify[1] = nullptr;
} }
+3
View File
@@ -114,7 +114,10 @@ namespace vm
if (ok) if (ok)
{ {
// Notify all waiters for an address
// This is because reservation_update is often brought after the actual reservation update and not by a fused operation
reservation_notifier_notify(addr, rtime); reservation_notifier_notify(addr, rtime);
reservation_notifier_notify(addr, rtime - 128);
if (cpu && !had_wait && cpu->test_stopped()) if (cpu && !had_wait && cpu->test_stopped())
{ {
+3 -4
View File
@@ -45,15 +45,14 @@ namespace vm
static inline atomic_t<reservation_waiter_t>* reservation_notifier(u32 raddr, u64 rtime) static inline atomic_t<reservation_waiter_t>* reservation_notifier(u32 raddr, u64 rtime)
{ {
rtime = 0;
constexpr u32 wait_vars_for_each = 64; constexpr u32 wait_vars_for_each = 64;
constexpr u32 unique_address_bit_mask = 0b111; constexpr u32 unique_address_bit_mask = 0b1111;
constexpr u32 unique_rtime_bit_mask = 0b11; constexpr u32 unique_rtime_bit_mask = 0b1;
extern std::array<atomic_t<reservation_waiter_t>, wait_vars_for_each * (unique_address_bit_mask + 1) * (unique_rtime_bit_mask + 1)> g_resrv_waiters_count; extern std::array<atomic_t<reservation_waiter_t>, wait_vars_for_each * (unique_address_bit_mask + 1) * (unique_rtime_bit_mask + 1)> g_resrv_waiters_count;
// Storage efficient method to distinguish different nearby addresses (which are likely) // Storage efficient method to distinguish different nearby addresses (which are likely)
const usz index = std::popcount(raddr & -1024) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 3) + ((raddr / 128) & unique_address_bit_mask); const usz index = std::popcount(raddr & -2048) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 4) + ((raddr / 128) & unique_address_bit_mask);
return &g_resrv_waiters_count[index]; return &g_resrv_waiters_count[index];
} }