Fix bugs with the scheduler

Selecting the next process and switching to it should be done in
a safe fashion. Otherwise, can be preempted between the two and the
decision may not be valid anymore
This commit is contained in:
Baptiste Wicht 2018-03-21 13:20:13 +01:00
parent f0661aaf46
commit aaa1f2a7a2

View File

@ -58,8 +58,6 @@ pcb_t pcb;
//Define one run queue for each priority level
std::array<std::vector<scheduler::pid_t>, scheduler::PRIORITY_LEVELS> run_queues;
int_lock queue_lock;
volatile bool started = false;
volatile size_t rr_quantum = 0;
@ -153,7 +151,8 @@ void gc_task(){
// 5. Remove process from run queue
{
std::lock_guard<int_lock> l(queue_lock);
// Move only write to the list with a lock
direct_int_lock queue_lock;
for(size_t index = 0; index < run_queue(desc.priority).size(); ++index){
if(run_queue(desc.priority)[index] == desc.pid){
@ -291,7 +290,8 @@ void queue_process(scheduler::pid_t pid){
process.state = scheduler::process_state::READY;
std::lock_guard<int_lock> l(queue_lock);
// Move only write to the list with a lock
direct_int_lock queue_lock;
run_queue(process.process.priority).push_back(pid);
}
@ -361,20 +361,26 @@ void create_post_init_task(){
logging::logf(logging::log_level::DEBUG, "scheduler: post_init_task %u \n", post_init_pid);
}
void switch_to_process(size_t pid){
if(pcb[current_pid].process.system){
verbose_logf(logging::log_level::DEBUG, "scheduler: Switch from %u (s:%u) to %u (rip:%u)\n", current_pid, static_cast<size_t>(pcb[current_pid].state), pid, pcb[current_pid].process.context->rip);
void switch_to_process_with_lock(size_t new_pid){
if (pcb[current_pid].process.system) {
verbose_logf(logging::log_level::DEBUG, "scheduler: Switch from %u (s:%u) to %u (rip:%u)\n",
current_pid, static_cast<size_t>(pcb[current_pid].state), new_pid, pcb[current_pid].process.context->rip);
} else {
verbose_logf(logging::log_level::DEBUG, "scheduler: Switch from %u (s:%u) to %u\n", current_pid, static_cast<size_t>(pcb[current_pid].state), pid);
verbose_logf(logging::log_level::DEBUG, "scheduler: Switch from %u (s:%u) to %u\n",
current_pid, static_cast<size_t>(pcb[current_pid].state), new_pid);
}
// This should never be interrupted
direct_int_lock l;
auto old_pid = current_pid;
current_pid = pid;
auto& process = pcb[pid];
// It is possible that preemption occured and that the process is already
// running, in which case, there is no need to do anything
if(old_pid == new_pid){
return;
}
current_pid = new_pid;
auto& process = pcb[new_pid];
process.state = scheduler::process_state::RUNNING;
gdt::tss().rsp0_low = process.process.kernel_rsp & 0xFFFFFFFF;
@ -383,10 +389,20 @@ void switch_to_process(size_t pid){
task_switch(old_pid, current_pid);
}
size_t select_next_process(){
auto current_priority = pcb[current_pid].process.priority;
void switch_to_process(size_t new_pid){
// This should never be interrupted
direct_int_lock l;
std::lock_guard<int_lock> l(queue_lock);
switch_to_process_with_lock(new_pid);
}
/*!
* \brief Select the next process to run.
*
* This function assume that an int_lock is already owned.
*/
size_t select_next_process_with_lock(){
auto current_priority = pcb[current_pid].process.priority;
//1. Run a process of higher priority, if any
for(size_t p = scheduler::MAX_PRIORITY; p > current_priority; --p){
@ -435,6 +451,14 @@ size_t select_next_process(){
thor_unreachable("No process is READY");
}
size_t select_next_process(){
auto current_priority = pcb[current_pid].process.priority;
return select_next_process_with_lock();
}
bool allocate_user_memory(scheduler::process_t& process, size_t address, size_t size, size_t& ref){
//1. Calculate some stuff
auto first_page = paging::page_align(address);
@ -858,17 +882,24 @@ void scheduler::tick(){
if(process.rounds == rr_quantum){
process.rounds = 0;
process.state = process_state::READY;
auto previous_state = process.state;
// Change to Ready if it was not blocked
// If it was blocked, we still prempt and it will end up in reschedule
// later but with a full time quanta
if(previous_state != process_state::BLOCKED && previous_state != process_state::BLOCKED_TIMEOUT){
process.state = process_state::READY;
}
auto pid = select_next_process();
//If it is the same, no need to go to the switching process
if(pid == current_pid){
process.state = process_state::RUNNING;
process.state = previous_state;
return;
}
verbose_logf(logging::log_level::DEBUG, "scheduler: Preempt %u to %u\n", current_pid, pid);
verbose_logf(logging::log_level::DEBUG, "scheduler: Preempt %u (%d->%d) to %u\n", current_pid, previous_state, process.state, pid);
switch_to_process(pid);
} else {
@ -880,14 +911,16 @@ void scheduler::tick(){
void scheduler::yield(){
thor_assert(started, "No interest in yielding before start");
thor_assert(pcb[current_pid].state == process_state::RUNNING, "Can only yield() running processes");
pcb[current_pid].state = process_state::READY;
auto pid = select_next_process();
direct_int_lock lock;
auto pid = select_next_process_with_lock();
if(pid != current_pid){
verbose_logf(logging::log_level::DEBUG, "scheduler: Yields %u to %u\n", current_pid, pid);
switch_to_process(pid);
switch_to_process_with_lock(pid);
} else {
pcb[current_pid].state = process_state::RUNNING;
}
@ -900,9 +933,11 @@ void scheduler::reschedule(){
//The process just got blocked or put to sleep, choose another one
if(process.state != process_state::RUNNING){
auto index = select_next_process();
direct_int_lock lock;
switch_to_process(index);
auto index = select_next_process_with_lock();
switch_to_process_with_lock(index);
}
//At this point we just have to return to the current process