From 06f4dfb9f1a22a3cb80fe2f8905d6b336fc47c35 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 30 Jul 2021 18:48:32 +0300 Subject: [PATCH] SPU: Implement sys_spu_initialize and limit of physical SPU threads --- rpcs3/Emu/Cell/lv2/sys_spu.cpp | 95 ++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 66e946e41a..8bf4b62282 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -198,17 +198,82 @@ std::pair*, std::shared_ptr> lv2_spu_gro return res; } +namespace +{ + +struct limits_data +{ + u32 physical = 0; + u32 raw_spu = 0; + u32 controllable = 0; + u32 spu_limit = umax; + u32 raw_limit = umax; +}; + +struct spu_limits_t +{ + u32 max_raw = 0; + u32 max_spu = 6; + shared_mutex mutex; + + bool check(const limits_data& init) const + { + u32 physical_spus_count = init.physical; + u32 raw_spu_count = init.raw_spu; + u32 controllable_spu_count = init.controllable; + const u32 spu_limit = init.spu_limit != umax ? init.spu_limit : max_spu; + const u32 raw_limit = init.raw_limit != umax ? init.raw_limit : max_raw; + + idm::select([&](u32, lv2_spu_group& group) + { + if (group.has_scheduler_context) + { + controllable_spu_count = std::max(controllable_spu_count, group.max_num); + } + else + { + physical_spus_count += group.max_num; + } + }); + + raw_spu_count += spu_thread::g_raw_spu_ctr; + + if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count + controllable_spu_count > spu_limit) + { + return false; + } + + return true; + } +}; + +} // annonymous namespace + error_code sys_spu_initialize(ppu_thread& ppu, u32 max_usable_spu, u32 max_raw_spu) { ppu.state += cpu_flag::wait; sys_spu.warning("sys_spu_initialize(max_usable_spu=%d, max_raw_spu=%d)", max_usable_spu, max_raw_spu); + auto& limits = g_fxo->get(); + if (max_raw_spu > 5) { return CELL_EINVAL; } + // NOTE: This value can be changed by VSH in theory + max_usable_spu = 6; + + std::lock_guard lock(limits.mutex); + + if (!limits.check(limits_data{.spu_limit = max_usable_spu - max_raw_spu, .raw_limit = max_raw_spu})) + { + return CELL_EBUSY; + } + + limits.max_raw = max_raw_spu; + limits.max_spu = max_usable_spu - max_raw_spu; return CELL_OK; } @@ -505,7 +570,7 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num bool use_scheduler = true; bool use_memct = !!(type & SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER); bool needs_root = false; - u32 max_threads = 6; // TODO: max num value should be affected by sys_spu_initialize() settings + u32 max_threads = 6; u32 min_threads = 1; u32 mem_size = 0; lv2_memory_container* ct{}; @@ -620,6 +685,16 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num } } + auto& limits = g_fxo->get(); + + std::lock_guard lock(limits.mutex); + + if (!limits.check(use_scheduler ? limits_data{.controllable = num} : limits_data{.physical = num})) + { + ct->used -= mem_size; + return CELL_EBUSY; + } + const auto group = idm::make_ptr(std::string(attr->name.get_ptr(), std::max(attr->nsize, 1) - 1), num, prio, type, ct, use_scheduler, mem_size); if (!group) @@ -1821,7 +1896,14 @@ error_code sys_raw_spu_create(ppu_thread& ppu, vm::ptr id, vm::ptr at sys_spu.warning("sys_raw_spu_create(id=*0x%x, attr=*0x%x)", id, attr); - // TODO: check number set by sys_spu_initialize() + auto& limits = g_fxo->get(); + + std::lock_guard lock(limits.mutex); + + if (!limits.check(limits_data{.raw_spu = 1})) + { + return CELL_EAGAIN; + } if (!spu_thread::g_raw_spu_ctr.try_inc(5)) { @@ -1867,7 +1949,14 @@ error_code sys_isolated_spu_create(ppu_thread& ppu, vm::ptr id, vm::ptrget(); + + std::lock_guard lock(limits.mutex); + + if (!limits.check(limits_data{.raw_spu = 1})) + { + return CELL_EAGAIN; + } if (!spu_thread::g_raw_spu_ctr.try_inc(5)) {