sys_spu: Fix HW schedular limits

This commit is contained in:
elad335 2025-03-29 18:56:14 +03:00 committed by Elad
parent f38a61bdb3
commit 19d01835d3

View file

@ -388,19 +388,43 @@ struct spu_limits_t
SAVESTATE_INIT_POS(47);
bool check(const limits_data& init) const
bool check_valid(const limits_data& init) const
{
const u32 physical_spus_count = init.physical;
const 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;
if (spu_limit + raw_limit > 6 || physical_spus_count > spu_limit || controllable_spu_count > spu_limit)
{
return false;
}
return true;
}
bool check_busy(const limits_data& init) const
{
u32 physical_spus_count = init.physical;
u32 raw_spu_count = init.raw_spu;
u32 controllable_spu_count = init.controllable;
u32 system_coop = init.controllable != 0 && init.physical != 0 ? 1 : 0;
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<lv2_spu_group>([&](u32, lv2_spu_group& group)
{
if (group.has_scheduler_context)
if (group.type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM)
{
controllable_spu_count = std::max(controllable_spu_count, group.max_num);
system_coop++;
controllable_spu_count = std::max<u32>(controllable_spu_count, 1);
physical_spus_count += group.max_num - 1;
}
else if (group.has_scheduler_context)
{
controllable_spu_count = std::max<u32>(controllable_spu_count, group.max_num);
}
else
{
@ -410,11 +434,18 @@ struct spu_limits_t
raw_spu_count += spu_thread::g_raw_spu_ctr;
if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count >= spu_limit || physical_spus_count + controllable_spu_count > spu_limit)
// physical_spus_count >= spu_limit returns EBUSY, not EINVAL!
if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count >= spu_limit || physical_spus_count > spu_limit || controllable_spu_count > spu_limit)
{
return false;
}
if (system_coop > 1)
{
// Cannot have more than one SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM group at a time
return false;
}
return true;
}
};
@ -437,7 +468,7 @@ error_code sys_spu_initialize(ppu_thread& ppu, u32 max_usable_spu, u32 max_raw_s
std::lock_guard lock(limits.mutex);
if (!limits.check(limits_data{.spu_limit = max_usable_spu - max_raw_spu, .raw_limit = max_raw_spu}))
if (!limits.check_busy(limits_data{.spu_limit = max_usable_spu - max_raw_spu, .raw_limit = max_raw_spu}))
{
return CELL_EBUSY;
}
@ -845,23 +876,24 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr<u32> id, u32 num
switch (type)
{
case 0x0:
case 0x4:
case 0x18:
case SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER:
case SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT:
{
break;
}
case 0x20:
case 0x22:
case 0x24:
case 0x26:
case SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM:
case (SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM | 0x2):
case (SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM | 0x4):
case (SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM | 0x6):
{
if (type == 0x22 || type == 0x26)
{
needs_root = true;
}
min_threads = 2; // That's what appears from reversing
// For a single thread that is being shared with system (the cooperative victim)
min_threads = 2;
break;
}
@ -901,7 +933,7 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr<u32> id, u32 num
if (is_system_coop)
{
// Constant size, unknown what it means
// For a single thread that is being shared with system (the cooperative victim)
mem_size = SPU_LS_SIZE;
}
else if (type & SYS_SPU_THREAD_GROUP_TYPE_NON_CONTEXT)
@ -952,7 +984,29 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr<u32> id, u32 num
std::unique_lock lock(limits.mutex);
if (!limits.check(use_scheduler ? limits_data{.controllable = num} : limits_data{.physical = num}))
limits_data group_limits{};
if (is_system_coop)
{
group_limits.controllable = 1;
group_limits.physical = num - 1;
}
else if (use_scheduler)
{
group_limits.controllable = num;
}
else
{
group_limits.physical = num;
}
if (!limits.check_valid(group_limits))
{
ct->free(mem_size);
return CELL_EINVAL;
}
if (!limits.check_busy(group_limits))
{
ct->free(mem_size);
return CELL_EBUSY;
@ -1135,6 +1189,11 @@ error_code sys_spu_thread_group_suspend(ppu_thread& ppu, u32 id)
return CELL_EINVAL;
}
if (group->type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM)
{
return CELL_EINVAL;
}
std::lock_guard lock(group->mutex);
CellError error;
@ -1218,6 +1277,11 @@ error_code sys_spu_thread_group_resume(ppu_thread& ppu, u32 id)
return CELL_EINVAL;
}
if (group->type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM)
{
return CELL_EINVAL;
}
struct notify_on_exit
{
usz index = umax;
@ -1565,6 +1629,11 @@ error_code sys_spu_thread_group_set_priority(ppu_thread& ppu, u32 id, s32 priori
return CELL_EINVAL;
}
if (group->type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM)
{
return CELL_EINVAL;
}
group->prio.atomic_op([&](std::common_type_t<decltype(lv2_spu_group::prio)>& prio)
{
prio.prio = priority;
@ -1592,6 +1661,11 @@ error_code sys_spu_thread_group_get_priority(ppu_thread& ppu, u32 id, vm::ptr<s3
{
*priority = 0;
}
else if (group->type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM)
{
// Regardless of the value being set in group creation
*priority = 15;
}
else
{
*priority = group->prio.load().prio;
@ -2275,7 +2349,7 @@ error_code sys_raw_spu_create(ppu_thread& ppu, vm::ptr<u32> id, vm::ptr<void> at
std::lock_guard lock(limits.mutex);
if (!limits.check(limits_data{.raw_spu = 1}))
if (!limits.check_busy(limits_data{.raw_spu = 1}))
{
return CELL_EAGAIN;
}
@ -2331,7 +2405,7 @@ error_code sys_isolated_spu_create(ppu_thread& ppu, vm::ptr<u32> id, vm::ptr<voi
std::lock_guard lock(limits.mutex);
if (!limits.check(limits_data{.raw_spu = 1}))
if (!limits.check_busy(limits_data{.raw_spu = 1}))
{
return CELL_EAGAIN;
}