681 lines
22 KiB
C
681 lines
22 KiB
C
/// MentOS, The Mentoring Operating system project
|
|
/// @file scheduler.c
|
|
/// @brief Scheduler structures and functions.
|
|
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
|
|
/// See LICENSE.md for details.
|
|
|
|
/// Change the header.
|
|
#define __DEBUG_HEADER__ "[SCHED ]"
|
|
|
|
#include "assert.h"
|
|
#include "strerror.h"
|
|
#include "fs/vfs.h"
|
|
#include "process/scheduler.h"
|
|
#include "descriptor_tables/tss.h"
|
|
#include "devices/fpu.h"
|
|
#include "process/prio.h"
|
|
#include "process/wait.h"
|
|
#include "mem/kheap.h"
|
|
#include "system/panic.h"
|
|
#include "misc/debug.h"
|
|
#include "time.h"
|
|
#include "sys/errno.h"
|
|
#include "klib/list_head.h"
|
|
#include "mem/paging.h"
|
|
#include "hardware/timer.h"
|
|
#include "math.h"
|
|
#include "stdio.h"
|
|
|
|
/// @brief Assembly function setting the kernel stack to jump into
|
|
/// location in Ring 3 mode (USER mode).
|
|
/// @param location The location where to jump.
|
|
/// @param stack The stack to use.
|
|
extern void enter_userspace(uintptr_t location, uintptr_t stack);
|
|
|
|
/// The list of processes.
|
|
runqueue_t runqueue;
|
|
|
|
void scheduler_initialize()
|
|
{
|
|
// Initialize the runqueue list of tasks.
|
|
list_head_init(&runqueue.queue);
|
|
// Reset the current task.
|
|
runqueue.curr = NULL;
|
|
// Reset the number of active tasks.
|
|
runqueue.num_active = 0;
|
|
}
|
|
|
|
uint32_t scheduler_getpid(void)
|
|
{
|
|
/// The current unused PID.
|
|
static unsigned long int tid = 1;
|
|
|
|
// Return the pid and increment.
|
|
return tid++;
|
|
}
|
|
|
|
task_struct *scheduler_get_current_process()
|
|
{
|
|
return runqueue.curr;
|
|
}
|
|
|
|
time_t scheduler_get_maximum_vruntime()
|
|
{
|
|
time_t vruntime = 0;
|
|
task_struct *entry;
|
|
list_for_each_decl(it, &runqueue.queue)
|
|
{
|
|
// Check if we reached the head of list_head, and skip it.
|
|
if (it == &runqueue.queue)
|
|
continue;
|
|
// Get the current entry.
|
|
entry = list_entry(it, task_struct, run_list);
|
|
// Skip the process if it is a periodic one, we are issued to skip
|
|
// periodic tasks, and the entry is not a periodic task under
|
|
// analysis.
|
|
if (entry->se.is_periodic && !entry->se.is_under_analysis)
|
|
continue;
|
|
if (entry->se.vruntime > vruntime)
|
|
vruntime = entry->se.vruntime;
|
|
}
|
|
return vruntime;
|
|
}
|
|
|
|
size_t scheduler_get_active_processes()
|
|
{
|
|
return runqueue.num_active;
|
|
}
|
|
|
|
task_struct *scheduler_get_running_process(pid_t pid)
|
|
{
|
|
task_struct *entry;
|
|
list_for_each_decl(it, &runqueue.queue)
|
|
{
|
|
entry = list_entry(it, task_struct, run_list);
|
|
if (entry->pid == pid)
|
|
return entry;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void scheduler_enqueue_task(task_struct *process)
|
|
{
|
|
// If current_process is NULL, then process is the current process.
|
|
if (runqueue.curr == NULL) {
|
|
runqueue.curr = process;
|
|
}
|
|
// Add the new process at the end.
|
|
list_head_add_tail(&process->run_list, &runqueue.queue);
|
|
// Increment the number of active processes.
|
|
++runqueue.num_active;
|
|
}
|
|
|
|
void scheduler_dequeue_task(task_struct *process)
|
|
{
|
|
// Delete the process from the list of running processes.
|
|
list_head_del(&process->run_list);
|
|
// Decrement the number of active processes.
|
|
--runqueue.num_active;
|
|
if (process->se.is_periodic)
|
|
runqueue.num_periodic--;
|
|
}
|
|
|
|
void scheduler_run(pt_regs *f)
|
|
{
|
|
// Check if there is a running process.
|
|
if (runqueue.curr == NULL)
|
|
return;
|
|
|
|
task_struct *next = NULL;
|
|
|
|
// Update the context of the current process.
|
|
scheduler_store_context(f, runqueue.curr);
|
|
|
|
// We check the existence of pending signals every time we finish
|
|
// handling an interrupt or an exception.
|
|
if (!do_signal(f)) {
|
|
#if 1
|
|
if (runqueue.curr->state == EXIT_ZOMBIE) {
|
|
//==== Handle Zombies =================================================
|
|
//pr_debug("Handle zombie %d\n", runqueue.curr->pid);
|
|
// get the next process after the current one
|
|
list_head *nNode = runqueue.curr->run_list.next;
|
|
// check if we reached the head of list_head
|
|
if (nNode == &runqueue.queue) {
|
|
nNode = nNode->next;
|
|
}
|
|
// get the task_struct
|
|
next = list_entry(nNode, task_struct, run_list);
|
|
// Remove the zombie task.
|
|
scheduler_dequeue_task(runqueue.curr);
|
|
assert(next && "No valid task selected after removing ZOMBIE.");
|
|
//=====================================================================
|
|
} else {
|
|
#endif
|
|
//==== Scheduling =====================================================
|
|
// If we are currently executing a periodic process, and this process
|
|
// has yet to complete, keep executing it.
|
|
#ifdef SCHEDULER_EDF
|
|
if (runqueue.curr->se.is_periodic)
|
|
if (!runqueue.curr->se.executed)
|
|
return;
|
|
#endif
|
|
// Pointer to the next process to be executed.
|
|
next = scheduler_pick_next_task(&runqueue);
|
|
//=====================================================================
|
|
}
|
|
// Check if the next and current processes are different.
|
|
if (next != runqueue.curr) {
|
|
// Copy into Kernel stack the next process's context.
|
|
scheduler_restore_context(next, f);
|
|
}
|
|
}
|
|
//==========================================================================
|
|
}
|
|
|
|
void scheduler_store_context(pt_regs *f, task_struct *process)
|
|
{
|
|
// Store the registers.
|
|
process->thread.regs = *f;
|
|
}
|
|
|
|
void scheduler_restore_context(task_struct *process, pt_regs *f)
|
|
{
|
|
// Switch to the next process.
|
|
runqueue.curr = process;
|
|
// Restore the registers.
|
|
*f = process->thread.regs;
|
|
// TODO: Explain paging switch (ring 0 doesn't need page switching)
|
|
// Switch to process page directory
|
|
paging_switch_directory_va(process->mm->pgd);
|
|
}
|
|
|
|
void scheduler_enter_user_jmp(uintptr_t location, uintptr_t stack)
|
|
{
|
|
// Reset stack pointer for kernel.
|
|
tss_set_stack(0x10, initial_esp);
|
|
|
|
// update start execution time.
|
|
runqueue.curr->se.start_runtime = timer_get_ticks();
|
|
|
|
// last context switch time.
|
|
runqueue.curr->se.exec_start = timer_get_ticks();
|
|
|
|
// Jump in location.
|
|
enter_userspace(location, stack);
|
|
}
|
|
|
|
/// @brief Awakens a sleeping process.
|
|
/// @param process The process that should be awakened
|
|
/// @param mode The type of wait (TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE).
|
|
/// @param sync Specifies if the wakeup should be synchronous.
|
|
/// @return 1 on success, 0 on failure.
|
|
static inline int try_to_wake_up(task_struct *process, int mode, int sync)
|
|
{
|
|
// Only tasks in the state TASK_UNINTERRUPTIBLE can be woke up
|
|
if (process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_STOPPED) {
|
|
//TODO: Recalc task priority
|
|
process->state = TASK_RUNNING;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int default_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync)
|
|
{
|
|
task_struct *p = wait->task;
|
|
return try_to_wake_up(p, mode, sync);
|
|
}
|
|
|
|
wait_queue_entry_t *sleep_on(wait_queue_head_t *wq)
|
|
{
|
|
// Save the sleeping process registers state
|
|
task_struct *sleeping_task = scheduler_get_current_process();
|
|
|
|
#if 0
|
|
pt_regs* f = get_current_interrupt_stack_frame();
|
|
scheduler_store_context(f, sleeping_task);
|
|
|
|
// Select next process in the runqueue as the current, restore it's context,
|
|
// we assume that the first process is init wich does not sleep (I hope).
|
|
// This is necessary to make the scheduler_run() in syscall_handler work.
|
|
task_struct *next = list_entry(runqueue.queue.next, task_struct, run_list);
|
|
assert((next != sleeping_task) && "The next selected process in the runqueue is the sleeping process");
|
|
scheduler_restore_context(next, f);
|
|
#endif
|
|
|
|
// Stops task from runqueue making it unrunnable
|
|
sleeping_task->state = TASK_UNINTERRUPTIBLE;
|
|
|
|
// Add sleeping process to sleep wait queue
|
|
wait_queue_entry_t *wait_entry = kmalloc(sizeof(struct wait_queue_entry_t));
|
|
init_waitqueue_entry(wait_entry, sleeping_task);
|
|
add_wait_queue(wq, wait_entry);
|
|
|
|
return wait_entry;
|
|
}
|
|
|
|
int is_orphaned_pgrp(pid_t gid)
|
|
{
|
|
pid_t sid = 0;
|
|
|
|
// Obtain SID of the group from a member
|
|
list_head *it;
|
|
list_for_each (it, &runqueue.queue) {
|
|
task_struct *task = list_entry(it, task_struct, run_list);
|
|
if (task->gid == gid) {
|
|
sid = task->sid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if the process leader of the session is alive
|
|
list_for_each (it, &runqueue.queue) {
|
|
task_struct *task = list_entry(it, task_struct, run_list);
|
|
if (task->pid == sid) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
pid_t sys_getpid()
|
|
{
|
|
// Get the current task.
|
|
if (runqueue.curr == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
|
|
// Return the process identifer of the process.
|
|
return runqueue.curr->pid;
|
|
}
|
|
|
|
pid_t sys_getsid(pid_t pid)
|
|
{
|
|
//If pid == 0 return SID of the calling process
|
|
if (pid == 0) {
|
|
if (runqueue.curr == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
// Return the session identifer of the process.
|
|
return runqueue.curr->sid;
|
|
}
|
|
//If != 0 get SID of the specified process
|
|
list_head *it;
|
|
list_for_each (it, &runqueue.queue) {
|
|
task_struct *task = list_entry(it, task_struct, run_list);
|
|
if (task->pid == pid)
|
|
{
|
|
if(runqueue.curr->sid != task->sid)
|
|
return -EPERM;
|
|
|
|
return task->sid;
|
|
}
|
|
}
|
|
return -ESRCH;
|
|
}
|
|
|
|
pid_t sys_setsid()
|
|
{
|
|
task_struct *task = runqueue.curr;
|
|
if (task == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
if (task->sid == task->pid)
|
|
{
|
|
pr_debug("Process %d is already a session leader.", task->pid);
|
|
return -EPERM;
|
|
}
|
|
|
|
task->sid = task->pid;
|
|
task->gid = task->pid;
|
|
|
|
return task->sid;
|
|
}
|
|
|
|
pid_t sys_getgid()
|
|
{
|
|
task_struct *curr = runqueue.curr;
|
|
if (curr == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
|
|
return curr->gid;
|
|
}
|
|
|
|
int sys_setgid(pid_t gid)
|
|
{
|
|
task_struct *curr = runqueue.curr;
|
|
if (curr == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
|
|
if (curr->gid == curr->pid)
|
|
pr_debug("Process %d is already a session leader.", task->pid);
|
|
|
|
curr->gid = curr->pid;
|
|
return 0;
|
|
}
|
|
|
|
pid_t sys_getppid()
|
|
{
|
|
// Get the current task.
|
|
if (runqueue.curr == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
if (runqueue.curr->parent == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
// Return the parent process identifer of the process.
|
|
return runqueue.curr->parent->pid;
|
|
}
|
|
|
|
int sys_nice(int increment)
|
|
{
|
|
// Get the current task.
|
|
if (runqueue.curr == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
|
|
if (increment < -40) {
|
|
increment = -40;
|
|
}
|
|
if (increment > 40) {
|
|
increment = 40;
|
|
}
|
|
|
|
int newNice = PRIO_TO_NICE(runqueue.curr->se.prio) + increment;
|
|
pr_debug("New nice value would be : %d\n", newNice);
|
|
|
|
if (newNice < MIN_NICE) {
|
|
newNice = MIN_NICE;
|
|
}
|
|
if (newNice > MAX_NICE) {
|
|
newNice = MAX_NICE;
|
|
}
|
|
|
|
if (PRIO_TO_NICE(runqueue.curr->se.prio) != newNice && newNice >= MIN_NICE && newNice <= MAX_NICE) {
|
|
runqueue.curr->se.prio = NICE_TO_PRIO(newNice);
|
|
}
|
|
int actualNice = PRIO_TO_NICE(runqueue.curr->se.prio);
|
|
|
|
pr_debug("Actual new nice value is: %d\n", actualNice);
|
|
|
|
return actualNice;
|
|
}
|
|
|
|
pid_t sys_waitpid(pid_t pid, int *status, int options)
|
|
{
|
|
// Get the current task.
|
|
if (runqueue.curr == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
|
|
/* For now we do not support waiting for processes inside the given
|
|
* process group (pid < -1).
|
|
*/
|
|
if ((pid < -1) || (pid == 0)) {
|
|
return -ESRCH;
|
|
}
|
|
if (pid == runqueue.curr->pid) {
|
|
return -ECHILD;
|
|
}
|
|
if (options != 0 && options != WNOHANG) {
|
|
return -EINVAL;
|
|
}
|
|
#if 0
|
|
if (status == NULL) {
|
|
return -EFAULT;
|
|
}
|
|
#endif
|
|
if (list_head_empty(&runqueue.curr->children)) {
|
|
return -ECHILD;
|
|
}
|
|
list_head *it;
|
|
list_for_each (it, &runqueue.curr->children) {
|
|
task_struct *entry = list_entry(it, task_struct, sibling);
|
|
if (entry == NULL) {
|
|
continue;
|
|
}
|
|
if (entry->state != EXIT_ZOMBIE) {
|
|
continue;
|
|
}
|
|
if ((pid > 1) && (entry->pid != pid)) {
|
|
continue;
|
|
}
|
|
// Save the pid to return.
|
|
pid_t ppid = entry->pid;
|
|
// Save the state (TODO: Improve status set).
|
|
if (status)
|
|
(*status) = entry->state;
|
|
// Finalize the VFS structures.
|
|
vfs_destroy_task(entry);
|
|
// Remove entry from children of parent.
|
|
list_head_del(&entry->sibling);
|
|
// Remove entry from the scheduling queue.
|
|
scheduler_dequeue_task(entry);
|
|
// Delete the task_struct.
|
|
kmem_cache_free(entry);
|
|
pr_debug("Process %d is freeing memory of process %d.\n", runqueue.curr->pid, ppid);
|
|
return ppid;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void sys_exit(int exit_code)
|
|
{
|
|
// Get the current task.
|
|
if (runqueue.curr == NULL) {
|
|
kernel_panic("There is no current process!");
|
|
}
|
|
|
|
// Get the process.
|
|
task_struct *init_proc = scheduler_get_running_process(1);
|
|
if (runqueue.curr == init_proc) {
|
|
kernel_panic("Init process cannot call sys_exit!");
|
|
}
|
|
|
|
// Set the termination code of the process.
|
|
runqueue.curr->exit_code = (exit_code << 8) & 0xFF00;
|
|
// Set the state of the process to zombie.
|
|
runqueue.curr->state = EXIT_ZOMBIE;
|
|
// Send a SIGCHLD to the parent process.
|
|
if (runqueue.curr->parent) {
|
|
int ret = sys_kill(runqueue.curr->parent->pid, SIGCHLD);
|
|
if (ret == -1) {
|
|
printf("[%d] %5d failed sending signal %d : %s\n", ret, runqueue.curr->parent->pid,
|
|
SIGCHLD, strerror(errno));
|
|
}
|
|
}
|
|
|
|
// If it has children, then init process has to take care of them.
|
|
if (!list_head_empty(&runqueue.curr->children)) {
|
|
pr_debug("Moving children of %s(%d) to init(%d): {\n",
|
|
runqueue.curr->name, runqueue.curr->pid, init_proc->pid);
|
|
// Change the parent.
|
|
pr_debug("Moving children (%d): {\n", init_proc->pid);
|
|
list_for_each_decl(it, &runqueue.curr->children)
|
|
{
|
|
task_struct *entry = list_entry(it, task_struct, sibling);
|
|
pr_debug(" [%d] %s\n", entry->pid, entry->name);
|
|
entry->parent = init_proc;
|
|
}
|
|
pr_debug("}\n");
|
|
// Plug the list of children.
|
|
list_head_merge(&init_proc->children, &runqueue.curr->children);
|
|
// Print the list of children.
|
|
pr_debug("New list of init children (%d): {\n", init_proc->pid);
|
|
list_for_each_decl(it, &init_proc->children)
|
|
{
|
|
task_struct *entry = list_entry(it, task_struct, sibling);
|
|
pr_debug(" [%d] %s\n", entry->pid, entry->name);
|
|
}
|
|
pr_debug("}\n");
|
|
}
|
|
// Free the space occupied by the stack.
|
|
destroy_process_image(runqueue.curr->mm);
|
|
// Debugging message.
|
|
pr_debug("Process %d exited with value %d\n", runqueue.curr->pid, exit_code);
|
|
}
|
|
|
|
int sys_sched_setparam(pid_t pid, const sched_param_t *param)
|
|
{
|
|
list_head *it;
|
|
// Iter over the runqueue to find the task
|
|
list_for_each (it, &runqueue.queue) {
|
|
task_struct *entry = list_entry(it, task_struct, run_list);
|
|
if (entry->pid == pid) {
|
|
if (!entry->se.is_periodic && param->is_periodic)
|
|
runqueue.num_periodic++;
|
|
else if (entry->se.is_periodic && !param->is_periodic)
|
|
runqueue.num_periodic--;
|
|
// Sets the parameters from param to the "se" struct parameters.
|
|
entry->se.prio = param->sched_priority;
|
|
entry->se.period = param->period;
|
|
entry->se.arrivaltime = param->arrivaltime;
|
|
entry->se.is_periodic = param->is_periodic;
|
|
entry->se.deadline = timer_get_ticks() + param->deadline;
|
|
entry->se.next_period = timer_get_ticks();
|
|
|
|
entry->se.is_under_analysis = true;
|
|
entry->se.executed = false;
|
|
return 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int sys_sched_getparam(pid_t pid, sched_param_t *param)
|
|
{
|
|
list_head *it;
|
|
// Iter over the runqueue to find the task
|
|
list_for_each (it, &runqueue.queue) {
|
|
task_struct *entry = list_entry(it, task_struct, run_list);
|
|
if (entry->pid == pid) {
|
|
//Sets the parameters from the "se" struct to param
|
|
param->sched_priority = entry->se.prio;
|
|
param->period = entry->se.period;
|
|
param->deadline = entry->se.deadline;
|
|
param->arrivaltime = entry->se.arrivaltime;
|
|
return 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// @brief Performs the response time analysis for the current list
|
|
/// of periodic processes.
|
|
/// @return 1 if scheduling periodic processes is feasable, 0 otherwise.
|
|
static int __response_time_analysis()
|
|
{
|
|
task_struct *entry, *previous;
|
|
time_t r, previous_r = 0;
|
|
list_for_each_decl(it, &runqueue.queue)
|
|
{
|
|
entry = list_entry(it, task_struct, run_list);
|
|
if (entry->se.is_periodic) {
|
|
// Put r equal to worst case exec because is the first point in time that the task could possibly complete
|
|
r = entry->se.worst_case_exec;
|
|
previous_r = 0;
|
|
// The analysis can be completed either missing the deadline or reaching a fixed point
|
|
while (r < entry->se.deadline && r != previous_r) {
|
|
previous_r = r;
|
|
r = entry->se.worst_case_exec;
|
|
list_for_each_decl(it2, &runqueue.queue)
|
|
{
|
|
previous = list_entry(it2, task_struct, run_list);
|
|
// Check the interferences of higher priority processes
|
|
if (previous->se.is_periodic && previous->se.period < entry->se.period) {
|
|
r += (int)ceil((double)previous_r / (double)previous->se.period) * previous->se.worst_case_exec;
|
|
pr_debug("%d += (%.2f / %.2f) * %d\n", r, (double)previous_r, (double)previous->se.period, previous->se.worst_case_exec);
|
|
pr_debug("Response Time Analysis -> [%s]vs[%s] R = %d\n\n", entry->name, previous->name, r);
|
|
}
|
|
}
|
|
}
|
|
// Feasibility of scheduler is guaranteed if and only if response time analysis is lower than deadline.
|
|
if (r > entry->se.deadline)
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int sys_waitperiod()
|
|
{
|
|
if (runqueue.curr) {
|
|
if (runqueue.curr->se.is_periodic) {
|
|
// Update the Worst Case Execution Time (WCET).
|
|
time_t wcet = timer_get_ticks() - runqueue.curr->se.exec_start;
|
|
if (runqueue.curr->se.worst_case_exec < wcet)
|
|
runqueue.curr->se.worst_case_exec = wcet;
|
|
// Update thye utilization factor.
|
|
runqueue.curr->se.utilization_factor = ((double)runqueue.curr->se.worst_case_exec / (double)runqueue.curr->se.period);
|
|
|
|
// If the task is under analysis, we need to test if the process can be
|
|
// placed with the other periodic tasks.
|
|
if (runqueue.curr->se.is_under_analysis) {
|
|
runqueue.curr->se.worst_case_exec = runqueue.curr->se.sum_exec_runtime;
|
|
bool_t is_not_schedulable = false;
|
|
#if defined(SCHEDULER_EDF)
|
|
double u = 0;
|
|
task_struct *entry;
|
|
list_for_each_decl(it, &runqueue.queue)
|
|
{
|
|
entry = list_entry(it, task_struct, run_list);
|
|
// Sum the utilization factor of all periodic tasks.
|
|
if (entry->se.is_periodic)
|
|
u += entry->se.utilization_factor;
|
|
}
|
|
if (u > 1) {
|
|
is_not_schedulable = true;
|
|
}
|
|
pr_debug("utilization factor = %f\n", u);
|
|
#elif defined(SCHEDULER_RM)
|
|
// Calculating least upper bound of utilization factor.
|
|
// For large amount of processes ulub asymptotically should reach ln(2).
|
|
double ulub = (runqueue.num_periodic * (pow(2, (1.0 / runqueue.num_periodic)) - 1));
|
|
double u = 0;
|
|
task_struct *entry;
|
|
list_for_each_decl(it, &runqueue.queue)
|
|
{
|
|
entry = list_entry(it, task_struct, run_list);
|
|
// Sum the utilization factor of all periodic tasks.
|
|
if (entry->se.is_periodic)
|
|
u += entry->se.utilization_factor;
|
|
}
|
|
// If the sum of utilization factor is bounded between ulub and 1 we need to calculate
|
|
// the response time analysis for each process.
|
|
if (u > 1) {
|
|
is_not_schedulable = true;
|
|
} else if (u <= ulub)
|
|
is_not_schedulable = false;
|
|
else
|
|
is_not_schedulable = __response_time_analysis();
|
|
#endif
|
|
// If it is not schedulable, we need to tell it to the process.
|
|
if (is_not_schedulable)
|
|
return -ENOTSCHEDULABLE;
|
|
|
|
// Otherwise, it is schedulable.
|
|
runqueue.curr->se.is_under_analysis = false;
|
|
|
|
// The task has been executed as non-periodic process so that his deadline is not been updated
|
|
// by the scheduling algorithm of periodic tasks. We need to update it manually.
|
|
runqueue.curr->se.next_period = timer_get_ticks();
|
|
runqueue.curr->se.deadline = timer_get_ticks() + runqueue.curr->se.period;
|
|
}
|
|
|
|
if (timer_get_ticks() > runqueue.curr->se.deadline)
|
|
pr_warning("%d > %d Missing deadline...\n", timer_get_ticks(), runqueue.curr->se.deadline);
|
|
|
|
// Tell the scheduler that we have executed the periodic process.
|
|
runqueue.curr->se.executed = true;
|
|
|
|
} else
|
|
pr_warning("An aperiodic task is calling `waitperiod`, ignoring...\n");
|
|
return 0;
|
|
}
|
|
return -ESRCH;
|
|
} |