kern: correct behavior when setting activity/core mask for pinned thread

This commit is contained in:
Michael Scire 2020-07-28 03:20:24 -07:00 committed by SciresM
parent 787964f7e7
commit f70ee67753
2 changed files with 124 additions and 21 deletions

View file

@ -47,7 +47,7 @@ namespace ams::kern::arch::arm64 {
MESOSPHERE_LOG("SP = %016lx\n", context->sp);
/* Dump the page tables. */
GetCurrentProcess().GetPageTable().DumpTable();
/* GetCurrentProcess().GetPageTable().DumpTable(); */
MESOSPHERE_PANIC("Unhandled Exception in User Mode\n");

View file

@ -555,8 +555,10 @@ namespace ams::kern {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->parent != nullptr);
MESOSPHERE_ASSERT(affinity_mask != 0);
{
KScopedLightLock lk(this->activity_pause_lock);
/* Set the core mask. */
{
KScopedSchedulerLock sl;
MESOSPHERE_ASSERT(this->num_core_migration_disables >= 0);
@ -598,7 +600,59 @@ namespace ams::kern {
}
}
/* TODO: Paused waiter list. */
/* Update the pinned waiter list. */
{
bool retry_update;
bool thread_is_pinned = false;
do {
/* Lock the scheduler. */
KScopedSchedulerLock sl;
/* Don't do any further management if our termination has been requested. */
R_SUCCEED_IF(this->IsTerminationRequested());
/* By default, we won't need to retry. */
retry_update = false;
/* Check if the thread is currently running. */
bool thread_is_current = false;
s32 thread_core;
for (thread_core = 0; thread_core < static_cast<s32>(cpu::NumCores); ++thread_core) {
if (Kernel::GetCurrentContext(thread_core).current_thread == this) {
thread_is_current = true;
break;
}
}
/* If the thread is currently running, check whether it's no longer allowed under the new mask. */
if (thread_is_current && ((1ul << thread_core) & affinity_mask) == 0) {
/* If the thread is pinned, we want to wait until it's not pinned. */
if (this->GetStackParameters().is_pinned) {
/* Verify that the current thread isn't terminating. */
R_UNLESS(!GetCurrentThread().IsTerminationRequested(), svc::ResultTerminationRequested());
/* Note that the thread was pinned. */
thread_is_pinned = true;
/* Wait until the thread isn't pinned any more. */
this->pinned_waiter_list.push_back(GetCurrentThread());
GetCurrentThread().SetState(ThreadState_Waiting);
} else {
/* If the thread isn't pinned, release the scheduler lock and retry until it's not current. */
retry_update = true;
}
}
} while (retry_update);
/* If the thread was pinned, it no longer is, and we should remove the current thread from our waiter list. */
if (thread_is_pinned) {
/* Lock the scheduler. */
KScopedSchedulerLock sl;
/* Remove from the list. */
this->pinned_waiter_list.erase(this->pinned_waiter_list.iterator_to(GetCurrentThread()));
}
}
return ResultSuccess();
}
@ -720,8 +774,12 @@ namespace ams::kern {
}
Result KThread::SetActivity(ams::svc::ThreadActivity activity) {
/* Lock ourselves and the scheduler. */
/* Lock ourselves. */
KScopedLightLock lk(this->activity_pause_lock);
/* Set the activity. */
{
/* Lock the scheduler. */
KScopedSchedulerLock sl;
/* Verify our state. */
@ -735,9 +793,6 @@ namespace ams::kern {
/* Suspend. */
this->RequestSuspend(SuspendType_Thread);
/* TODO: Paused waiter list. */
MESOSPHERE_UNIMPLEMENTED();
} else {
MESOSPHERE_ASSERT(activity == ams::svc::ThreadActivity_Runnable);
@ -747,6 +802,54 @@ namespace ams::kern {
/* Resume. */
this->Resume(SuspendType_Thread);
}
}
/* If the thread is now paused, update the pinned waiter list. */
if (activity == ams::svc::ThreadActivity_Paused) {
bool thread_is_pinned = false;
bool thread_is_current;
do {
/* Lock the scheduler. */
KScopedSchedulerLock sl;
/* Don't do any further management if our termination has been requested. */
R_SUCCEED_IF(this->IsTerminationRequested());
/* Check whether the thread is pinned. */
if (this->GetStackParameters().is_pinned) {
/* Verify that the current thread isn't terminating. */
R_UNLESS(!GetCurrentThread().IsTerminationRequested(), svc::ResultTerminationRequested());
/* Note that the thread was pinned and not current. */
thread_is_pinned = true;
thread_is_current = false;
/* Wait until the thread isn't pinned any more. */
this->pinned_waiter_list.push_back(GetCurrentThread());
GetCurrentThread().SetState(ThreadState_Waiting);
} else {
/* Check if the thread is currently running. */
/* If it is, we'll need to retry. */
thread_is_current = false;
for (auto i = 0; i < static_cast<s32>(cpu::NumCores); ++i) {
if (Kernel::GetCurrentContext(i).current_thread == this) {
thread_is_current = true;
break;
}
}
}
} while (thread_is_current);
/* If the thread was pinned, it no longer is, and we should remove the current thread from our waiter list. */
if (thread_is_pinned) {
/* Lock the scheduler. */
KScopedSchedulerLock sl;
/* Remove from the list. */
this->pinned_waiter_list.erase(this->pinned_waiter_list.iterator_to(GetCurrentThread()));
}
}
return ResultSuccess();
}