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); MESOSPHERE_LOG("SP = %016lx\n", context->sp);
/* Dump the page tables. */ /* Dump the page tables. */
GetCurrentProcess().GetPageTable().DumpTable(); /* GetCurrentProcess().GetPageTable().DumpTable(); */
MESOSPHERE_PANIC("Unhandled Exception in User Mode\n"); MESOSPHERE_PANIC("Unhandled Exception in User Mode\n");

View File

@ -555,8 +555,10 @@ namespace ams::kern {
MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->parent != nullptr); MESOSPHERE_ASSERT(this->parent != nullptr);
MESOSPHERE_ASSERT(affinity_mask != 0); MESOSPHERE_ASSERT(affinity_mask != 0);
KScopedLightLock lk(this->activity_pause_lock);
/* Set the core mask. */
{ {
KScopedLightLock lk(this->activity_pause_lock);
KScopedSchedulerLock sl; KScopedSchedulerLock sl;
MESOSPHERE_ASSERT(this->num_core_migration_disables >= 0); 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(); return ResultSuccess();
} }
@ -720,32 +774,81 @@ namespace ams::kern {
} }
Result KThread::SetActivity(ams::svc::ThreadActivity activity) { Result KThread::SetActivity(ams::svc::ThreadActivity activity) {
/* Lock ourselves and the scheduler. */ /* Lock ourselves. */
KScopedLightLock lk(this->activity_pause_lock); KScopedLightLock lk(this->activity_pause_lock);
KScopedSchedulerLock sl;
/* Verify our state. */ /* Set the activity. */
const auto cur_state = this->GetState(); {
R_UNLESS((cur_state == ThreadState_Waiting || cur_state == ThreadState_Runnable), svc::ResultInvalidState()); /* Lock the scheduler. */
KScopedSchedulerLock sl;
/* Either pause or resume. */ /* Verify our state. */
const auto cur_state = this->GetState();
R_UNLESS((cur_state == ThreadState_Waiting || cur_state == ThreadState_Runnable), svc::ResultInvalidState());
/* Either pause or resume. */
if (activity == ams::svc::ThreadActivity_Paused) {
/* Verify that we're not suspended. */
R_UNLESS(!this->IsSuspendRequested(SuspendType_Thread), svc::ResultInvalidState());
/* Suspend. */
this->RequestSuspend(SuspendType_Thread);
} else {
MESOSPHERE_ASSERT(activity == ams::svc::ThreadActivity_Runnable);
/* Verify that we're suspended. */
R_UNLESS(this->IsSuspendRequested(SuspendType_Thread), svc::ResultInvalidState());
/* Resume. */
this->Resume(SuspendType_Thread);
}
}
/* If the thread is now paused, update the pinned waiter list. */
if (activity == ams::svc::ThreadActivity_Paused) { if (activity == ams::svc::ThreadActivity_Paused) {
/* Verify that we're not suspended. */ bool thread_is_pinned = false;
R_UNLESS(!this->IsSuspendRequested(SuspendType_Thread), svc::ResultInvalidState()); bool thread_is_current;
do {
/* Lock the scheduler. */
KScopedSchedulerLock sl;
/* Suspend. */ /* Don't do any further management if our termination has been requested. */
this->RequestSuspend(SuspendType_Thread); R_SUCCEED_IF(this->IsTerminationRequested());
/* TODO: Paused waiter list. */ /* Check whether the thread is pinned. */
MESOSPHERE_UNIMPLEMENTED(); if (this->GetStackParameters().is_pinned) {
} else { /* Verify that the current thread isn't terminating. */
MESOSPHERE_ASSERT(activity == ams::svc::ThreadActivity_Runnable); R_UNLESS(!GetCurrentThread().IsTerminationRequested(), svc::ResultTerminationRequested());
/* Verify that we're suspended. */ /* Note that the thread was pinned and not current. */
R_UNLESS(this->IsSuspendRequested(SuspendType_Thread), svc::ResultInvalidState()); thread_is_pinned = true;
thread_is_current = false;
/* Resume. */ /* Wait until the thread isn't pinned any more. */
this->Resume(SuspendType_Thread); 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(); return ResultSuccess();