747 lines
22 KiB
C
747 lines
22 KiB
C
/// MentOS, The Mentoring Operating system project
|
|
/// @file timer.c
|
|
/// @brief Timer implementation.
|
|
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
|
|
/// See LICENSE.md for details.
|
|
|
|
#include "hardware/timer.h"
|
|
|
|
#include "klib/irqflags.h"
|
|
#include "process/scheduler.h"
|
|
#include "hardware/pic8259.h"
|
|
#include "io/port_io.h"
|
|
#include "stdint.h"
|
|
#include "mem/kheap.h"
|
|
#include "misc/debug.h"
|
|
#include "process/wait.h"
|
|
#include "drivers/rtc.h"
|
|
#include "descriptor_tables/isr.h"
|
|
#include "devices/fpu.h"
|
|
#include "system/signal.h"
|
|
#include "assert.h"
|
|
#include "sys/errno.h"
|
|
|
|
/// Number of ticks per seconds.
|
|
#define TICKS_PER_SECOND 1193
|
|
|
|
/// @defgroup picregs Programmable Interval Timer Registers
|
|
/// @brief The list of registers used to set the PIT.
|
|
/// @{
|
|
|
|
/// Channel 0 data port (read/write).
|
|
#define PIT_DATAREG0 0x40u
|
|
|
|
/// Channel 1 data port (read/write).
|
|
#define PIT_DATAREG1 0x41u
|
|
|
|
/// Channel 2 data port (read/write).
|
|
#define PIT_DATAREG2 0x42u
|
|
|
|
/// Mode/Command register (write only, a read is ignored).
|
|
#define PIT_COMREG 0x43u
|
|
|
|
/// @}
|
|
|
|
/// @brief Frequency divider value (1.193182 MHz).
|
|
#define PIT_DIVISOR 1193182
|
|
|
|
/// @brief Command used to configure the PIT.
|
|
/// @details
|
|
/// Bits Usage
|
|
/// 6 and 7 [Select channel]
|
|
/// 0 0 = Channel 0
|
|
/// 0 1 = Channel 1
|
|
/// 1 0 = Channel 2
|
|
/// 1 1 = Read-back command (8254 only)
|
|
/// 4 and 5 [Access mode]
|
|
/// 0 0 = Latch count value command
|
|
/// 0 1 = Access mode: lobyte only
|
|
/// 1 0 = Access mode: hibyte only
|
|
/// 1 1 = Access mode: lobyte/hibyte
|
|
/// 1 to 3 [Operating mode]
|
|
/// 0 0 0 = Mode 0 (interrupt on terminal count)
|
|
/// 0 0 1 = Mode 1 (hardware re-triggerable one-shot)
|
|
/// 0 1 0 = Mode 2 (rate generator)
|
|
/// 0 1 1 = Mode 3 (square wave generator)
|
|
/// 1 0 0 = Mode 4 (software triggered strobe)
|
|
/// 1 0 1 = Mode 5 (hardware triggered strobe)
|
|
/// 1 1 0 = Mode 2 (rate generator, same as 010b)
|
|
/// 1 1 1 = Mode 3 (square wave generator, same as 011b)
|
|
/// 0 [BCD/Binary mode]
|
|
/// 0 = 16-bit binary
|
|
/// 1 = four-digit BCD
|
|
///
|
|
/// Examples:
|
|
/// 0x36 = 00|11|011|0
|
|
/// 0x34 = 00|11|010|0
|
|
#define PIT_CONFIGURATION 0x34u
|
|
|
|
/// Mask used to set the divisor.
|
|
#define PIT_MASK 0xFFu
|
|
|
|
/// The number of ticks since the system started its execution.
|
|
static __volatile__ unsigned long timer_ticks = 0;
|
|
|
|
void timer_phase(const uint32_t hz)
|
|
{
|
|
// Calculate our divisor.
|
|
unsigned int divisor = PIT_DIVISOR / hz;
|
|
// Set our command byte 0x36.
|
|
outportb(PIT_COMREG, PIT_CONFIGURATION);
|
|
// Set low byte of divisor.
|
|
outportb(PIT_DATAREG0, divisor & PIT_MASK);
|
|
// Set high byte of divisor.
|
|
outportb(PIT_DATAREG0, (divisor >> 8u) & PIT_MASK);
|
|
}
|
|
|
|
void timer_handler(pt_regs *reg)
|
|
{
|
|
// Save current process fpu state.
|
|
switch_fpu();
|
|
// Check if a second has passed.
|
|
++timer_ticks;
|
|
// Update all timers
|
|
run_timer_softirq();
|
|
// Perform the schedule.
|
|
scheduler_run(reg);
|
|
// Restore fpu state.
|
|
unswitch_fpu();
|
|
// The ack is sent to PIC only when all handlers terminated!
|
|
pic8259_send_eoi(IRQ_TIMER);
|
|
}
|
|
|
|
void timer_install()
|
|
{
|
|
dynamic_timers_install();
|
|
|
|
// Set the timer phase.
|
|
timer_phase(TICKS_PER_SECOND);
|
|
// Installs 'timer_handler' to IRQ0.
|
|
irq_install_handler(IRQ_TIMER, timer_handler, "timer");
|
|
// Enable the IRQ of the timer.
|
|
pic8259_irq_enable(IRQ_TIMER);
|
|
}
|
|
|
|
uint64_t timer_get_seconds()
|
|
{
|
|
return timer_ticks / TICKS_PER_SECOND;
|
|
}
|
|
|
|
unsigned long timer_get_ticks()
|
|
{
|
|
return timer_ticks;
|
|
}
|
|
|
|
//======================================================================================
|
|
// Dynamics timers
|
|
|
|
/// Contains timer for each CPU (for now only one)
|
|
static tvec_base_t cpu_base = { 0 };
|
|
|
|
/// Contains all process waiting for a sleep
|
|
static wait_queue_head_t sleep_queue;
|
|
|
|
/// @brief Initialize dynamic timer system
|
|
void dynamic_timers_install()
|
|
{
|
|
#ifndef ENABLE_REAL_TIMER_SYSTEM
|
|
list_head_init(&cpu_base.list);
|
|
#endif
|
|
|
|
// Initialize tvec_base structure
|
|
tvec_base_t *base = &cpu_base;
|
|
base->timer_ticks = 0;
|
|
|
|
for(int i = 0; i < TVR_SIZE; ++i)
|
|
list_head_init(base->tv1.vec + i);
|
|
|
|
for(int i = 0; i < TVN_SIZE; ++i) {
|
|
|
|
list_head_init(base->tv2.vec + i);
|
|
list_head_init(base->tv3.vec + i);
|
|
list_head_init(base->tv4.vec + i);
|
|
list_head_init(base->tv5.vec + i);
|
|
}
|
|
|
|
// Initialize sleeping process list
|
|
list_head_init(&sleep_queue.task_list);
|
|
spinlock_init(&sleep_queue.lock);
|
|
}
|
|
|
|
/// Prints used slots of timer vector
|
|
static void __print_tvec_slots(tvec_base_t *base, int tv_index) {
|
|
|
|
if (tv_index < 0 || tv_index > 5)
|
|
return;
|
|
|
|
// Write buffer
|
|
char result[TVN_SIZE + 1];
|
|
result[TVN_SIZE] = '\0';
|
|
|
|
struct timer_vec* tv = NULL;
|
|
switch(tv_index) {
|
|
|
|
// Root
|
|
case 1: {
|
|
|
|
pr_debug("base->tv1.vec:");
|
|
for(int i = 0; i < TVR_SIZE; ++i) {
|
|
|
|
// New line in order to not clutter the screen
|
|
int index = i % TVN_SIZE;
|
|
if (i != 0 && index == 0)
|
|
pr_debug("\n\t%s", result);
|
|
|
|
if (!list_head_empty(base->tv1.vec + i))
|
|
result[index] = '1';
|
|
else
|
|
result[index] = '0';
|
|
}
|
|
|
|
// The last line
|
|
pr_debug("\n\t%s\n", result);
|
|
return;
|
|
|
|
} break;
|
|
|
|
// Normal
|
|
case 2: tv = &base->tv2; break;
|
|
case 3: tv = &base->tv3; break;
|
|
case 4: tv = &base->tv4; break;
|
|
case 5: tv = &base->tv5; break;
|
|
}
|
|
|
|
for(int i = 0; i < TVN_SIZE; ++i) {
|
|
if (list_head_empty(tv->vec + i))
|
|
result[i] = '0';
|
|
else
|
|
result[i] = '1';
|
|
}
|
|
|
|
pr_debug("base->tv%d.vec:\n\t%s\n", tv_index, result);
|
|
}
|
|
|
|
/// Dump all timer vector in base
|
|
static inline void __dump_all_tvec_slots(tvec_base_t *base) {
|
|
__print_tvec_slots(base, 1);
|
|
__print_tvec_slots(base, 2);
|
|
__print_tvec_slots(base, 3);
|
|
__print_tvec_slots(base, 4);
|
|
__print_tvec_slots(base, 5);
|
|
}
|
|
|
|
/// Select correct timer vector and position inside of it for the input timer
|
|
/// index contains the position inside of the tv_index timer vector
|
|
static void __find_tvec(tvec_base_t *base, struct timer_list *timer, int* index, int* tv_index)
|
|
{
|
|
assert(index && "index is NULL");
|
|
assert(tv_index && "tv_index is NULL");
|
|
|
|
unsigned long expires = timer->expires;
|
|
unsigned long ticks = expires - base->timer_ticks;
|
|
|
|
unsigned long tv1_ticks = TIMER_TICKS(0);
|
|
unsigned long tv2_ticks = TIMER_TICKS(1);
|
|
unsigned long tv3_ticks = TIMER_TICKS(2);
|
|
unsigned long tv4_ticks = TIMER_TICKS(3);
|
|
|
|
// Can happen if you add a timer with expires == ticks, or in the past
|
|
if ((signed long)ticks < 0) {
|
|
*index = base->timer_ticks & TVR_MASK;
|
|
*tv_index = 1;
|
|
}
|
|
// tv1
|
|
else if (ticks < tv1_ticks) {
|
|
*index = expires & TVR_MASK;
|
|
*tv_index = 1;
|
|
}
|
|
// tv2
|
|
else if (ticks < tv2_ticks) {
|
|
*index = (expires >> TIMER_TICKS_BITS(0)) & TVN_MASK;
|
|
*tv_index = 2;
|
|
}
|
|
// tv3
|
|
else if (ticks < tv3_ticks) {
|
|
*index = (expires >> TIMER_TICKS_BITS(1)) & TVN_MASK;
|
|
*tv_index = 3;
|
|
}
|
|
// tv4
|
|
else if (ticks < tv4_ticks) {
|
|
*index = (expires >> TIMER_TICKS_BITS(2)) & TVN_MASK;
|
|
*tv_index = 4;
|
|
}
|
|
// tv5
|
|
else {
|
|
*index = (expires >> TIMER_TICKS_BITS(3)) & TVN_MASK;
|
|
*tv_index = 5;
|
|
}
|
|
}
|
|
|
|
/// Add timers into different lists based on their expire time
|
|
static void __add_timer_tvec_base(tvec_base_t *base, struct timer_list *timer) {
|
|
|
|
int index = 0, tv_index = 0;
|
|
__find_tvec(base, timer, &index, &tv_index);
|
|
|
|
struct list_head* vec;
|
|
switch(tv_index) {
|
|
case 1: vec = base->tv1.vec + index; break;
|
|
case 2: vec = base->tv2.vec + index; break;
|
|
case 3: vec = base->tv3.vec + index; break;
|
|
case 4: vec = base->tv4.vec + index; break;
|
|
case 5: vec = base->tv5.vec + index; break;
|
|
}
|
|
|
|
pr_debug("Adding timer at time_index: %d in tv%d\n", index, tv_index);
|
|
list_head_add_tail(&timer->entry, vec);
|
|
|
|
#ifdef ENABLE_REAL_TIMER_SYSTEM_DUMP
|
|
__dump_all_tvec_slots(base);
|
|
#endif
|
|
|
|
}
|
|
|
|
/// Remove timer from tvec_base
|
|
static void __rem_timer_tvec_base(tvec_base_t *base, struct timer_list *timer) {
|
|
|
|
int index = 0, tv_index = 0;
|
|
__find_tvec(base, timer, &index, &tv_index);
|
|
|
|
struct list_head* vec;
|
|
switch(tv_index) {
|
|
case 1: vec = base->tv1.vec + index; break;
|
|
case 2: vec = base->tv2.vec + index; break;
|
|
case 3: vec = base->tv3.vec + index; break;
|
|
case 4: vec = base->tv4.vec + index; break;
|
|
case 5: vec = base->tv5.vec + index; break;
|
|
}
|
|
|
|
pr_debug("Removing timer at time_index: %d in tv%d\n", index, tv_index);
|
|
list_head_del(&timer->entry);
|
|
|
|
#ifdef ENABLE_REAL_TIMER_SYSTEM_DUMP
|
|
__dump_all_tvec_slots(base);
|
|
#endif
|
|
|
|
}
|
|
|
|
/// Move all timers from tv up one level
|
|
static int cascate(tvec_base_t* base, timer_vec* tv, int time_index, int tv_index) {
|
|
|
|
if (!list_head_empty(tv->vec + time_index)) {
|
|
pr_debug("Relocating timers in tv%d.vec[%d]\n", tv_index, time_index);
|
|
|
|
// Reinsert all timers into base in the new correct list
|
|
struct list_head *it, *tmp;
|
|
list_for_each_safe (it, tmp, tv->vec + time_index) {
|
|
struct timer_list *timer = list_entry(it, struct timer_list, entry);
|
|
list_head_del(it);
|
|
|
|
__add_timer_tvec_base(base, timer);
|
|
}
|
|
|
|
}
|
|
return time_index;
|
|
}
|
|
|
|
void run_timer_softirq()
|
|
{
|
|
tvec_base_t *base = &cpu_base;
|
|
spinlock_lock(&base->lock);
|
|
|
|
#ifdef ENABLE_REAL_TIMER_SYSTEM
|
|
|
|
// While we are not up to date with current ticks
|
|
unsigned long current_ticks = timer_get_ticks();
|
|
while (base->timer_ticks <= current_ticks) {
|
|
|
|
// Index of the current timer to execute
|
|
int current_time_index = base->timer_ticks & TVR_MASK;
|
|
|
|
// If the index is zero then all lists in base->tv1 have been checked, so they are empty
|
|
if (!current_time_index) {
|
|
|
|
// Consider the first invocation of the cascade() function: it receives as arguments
|
|
// the address in base, the address of base->tv2, and the index of the list
|
|
// in base->tv2 including the timers that will decay in the next 256 ticks. This
|
|
// index is determined by looking at the proper bits of the base->timer_ticks value.
|
|
// cascade() moves all dynamic timers in the base->tv2 list into the
|
|
// proper lists of base->tv1; then, it returns a positive value, unless all base->tv2
|
|
// lists are now empty. If so, cascade() is invoked once more to replenish
|
|
// base->tv2 with the timers included in a list of base->tv3, and so on.
|
|
|
|
int tv2_index = (base->timer_ticks >> TIMER_TICKS_BITS(0)) & TVN_MASK;
|
|
int tv3_index = (base->timer_ticks >> TIMER_TICKS_BITS(1)) & TVN_MASK;
|
|
int tv4_index = (base->timer_ticks >> TIMER_TICKS_BITS(2)) & TVN_MASK;
|
|
int tv5_index = (base->timer_ticks >> TIMER_TICKS_BITS(3)) & TVN_MASK;
|
|
|
|
if (!cascate(base, &base->tv2, tv2_index, 2) &&
|
|
!cascate(base, &base->tv3, tv3_index, 3) &&
|
|
!cascate(base, &base->tv4, tv4_index, 4) &&
|
|
!cascate(base, &base->tv5, tv5_index, 5));
|
|
}
|
|
|
|
// If there are timers to execute in this instant
|
|
if (!list_head_empty(&base->tv1.vec[current_time_index])) {
|
|
pr_notice("Executing dynamic timers at %d ticks from start inside of tv1.vec[%d]\n",
|
|
base->timer_ticks, current_time_index);
|
|
|
|
// Trigger all timers
|
|
struct list_head *it, *tmp;
|
|
list_for_each_safe (it, tmp, &base->tv1.vec[current_time_index]) {
|
|
struct timer_list *timer = list_entry(it, struct timer_list, entry);
|
|
|
|
// Executes timer function
|
|
spinlock_unlock(&base->lock);
|
|
pr_notice("Executing dynamic timer function...\n");
|
|
timer->function(timer->data);
|
|
spinlock_lock(&base->lock);
|
|
|
|
// Removes timer from list
|
|
list_head_del(it);
|
|
kfree(timer);
|
|
}
|
|
}
|
|
|
|
// Advance timer check
|
|
++base->timer_ticks;
|
|
}
|
|
|
|
base->running_timer = NULL;
|
|
|
|
#else
|
|
|
|
struct list_head *it, *tmp;
|
|
list_for_each_safe (it, tmp, &base->list) {
|
|
struct timer_list *timer = list_entry(it, struct timer_list, entry);
|
|
if (timer->expires <= timer_get_ticks()) {
|
|
base->running_timer = timer;
|
|
timer->base = NULL;
|
|
|
|
// Executes timer function
|
|
spinlock_unlock(&base->lock);
|
|
pr_notice("Executing dynamic timer function...\n");
|
|
timer->function(timer->data);
|
|
spinlock_lock(&base->lock);
|
|
|
|
// Removes timer from list
|
|
pr_notice("Removing dynamic timer...\n");
|
|
list_head_del(it);
|
|
kfree(timer);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
base->running_timer = NULL;
|
|
spinlock_unlock(&base->lock);
|
|
}
|
|
|
|
void init_timer(struct timer_list *timer)
|
|
{
|
|
timer->base = NULL;
|
|
list_head_init(&timer->entry);
|
|
spinlock_unlock(&timer->lock);
|
|
}
|
|
|
|
void add_timer(struct timer_list *timer)
|
|
{
|
|
tvec_base_t *base = &cpu_base;
|
|
timer->base = base;
|
|
|
|
#ifdef ENABLE_REAL_TIMER_SYSTEM
|
|
__add_timer_tvec_base(base, timer);
|
|
#else
|
|
list_head_add_tail(&timer->entry, &base->list);
|
|
#endif
|
|
|
|
}
|
|
|
|
void del_timer(struct timer_list *timer)
|
|
{
|
|
tvec_base_t *base = &cpu_base;
|
|
timer->base = NULL;
|
|
|
|
#ifdef ENABLE_REAL_TIMER_SYSTEM
|
|
__rem_timer_tvec_base(base, timer);
|
|
#else
|
|
list_head_del(&timer->entry);
|
|
#endif
|
|
|
|
}
|
|
|
|
//======================================================================================
|
|
// Sleep
|
|
|
|
/// @brief Debugging function.
|
|
/// @param data The data.
|
|
static inline void debug_timeout(unsigned long data)
|
|
{
|
|
pr_notice("Il timer è stato attivato con successo: %d, ticks: %d, seconds: %d\n",
|
|
data, timer_ticks, timer_get_seconds());
|
|
}
|
|
|
|
/// @brief Contains the entry of a wait queue and timespec which keeps trakc of
|
|
/// the remaining time.
|
|
typedef struct sleep_data_t {
|
|
/// POinter to the entry of a wait queue.
|
|
wait_queue_entry_t *entry;
|
|
/// Keeps track of the remaining time.
|
|
timespec *rem;
|
|
} sleep_data_t;
|
|
|
|
/// @brief Callback for when a sleep timer expires
|
|
/// @param data Custom data stored in the timer
|
|
void sleep_timeout(unsigned long data)
|
|
{
|
|
// NOTE: We could modify the sleep_on and make it return the wait_queue_entry_t
|
|
// and then store it in the dynamic timer data member instead of the task pid,
|
|
// this would remove the need to iterate the sleep queue list.
|
|
|
|
sleep_data_t *sleep_data = (sleep_data_t *)data;
|
|
|
|
wait_queue_entry_t *entry = sleep_data->entry;
|
|
task_struct *task = entry->task;
|
|
|
|
// Executed entry's wakeup test function
|
|
int res = entry->func(entry, 0, 0);
|
|
if (res == 1) {
|
|
// Removes entry from list and memory
|
|
remove_wait_queue(&sleep_queue, entry);
|
|
kfree(entry);
|
|
|
|
pr_debug("Process (pid: %d) restored from sleep\n", task->pid);
|
|
}
|
|
}
|
|
|
|
int sys_nanosleep(const timespec *req, timespec *rem)
|
|
{
|
|
// Probabilmente devi salvare rem da qualche parte, perche' dentro ci va
|
|
// messo quanto tempo mancava allo scadere del timer nel caso in cui il
|
|
// timer venga interrotto prima da un segnale.
|
|
pr_debug("sys_nanosleep([s:%d; ns:%d],...)\n", req->tv_sec, req->tv_nsec);
|
|
|
|
// Saves pid and rem timespec
|
|
sleep_data_t *data = kmalloc(sizeof(sleep_data_t));
|
|
data->rem = rem;
|
|
|
|
// Create a dinamic timer to wake up the process after some time
|
|
struct timer_list *sleep_timer = kmalloc(sizeof(struct timer_list));
|
|
init_timer(sleep_timer);
|
|
|
|
sleep_timer->expires = timer_get_ticks() + TICKS_PER_SECOND * req->tv_sec;
|
|
sleep_timer->function = &sleep_timeout;
|
|
sleep_timer->data = (unsigned long)data;
|
|
|
|
// Removes current process from runqueue and stores it in the waiting queue,
|
|
// this must be done at the end, because it changes the current active page
|
|
// and invalidates the req and rem pointers (?)
|
|
wait_queue_entry_t *entry = sleep_on(&sleep_queue);
|
|
data->entry = entry;
|
|
|
|
add_timer(sleep_timer);
|
|
return -1;
|
|
}
|
|
|
|
/// @brief Function executed when the real_timer of a process expires, sends SIGALRM to process.
|
|
/// @param pid PID of the process whos associated timer has expired
|
|
void alarm_timeout(unsigned long pid)
|
|
{
|
|
sys_kill(pid, SIGALRM);
|
|
|
|
struct task_struct *cur = scheduler_get_current_process();
|
|
cur->real_timer = NULL;
|
|
}
|
|
|
|
int sys_alarm(int seconds)
|
|
{
|
|
pr_debug("sys_alarm(seconds:%d)\n", seconds);
|
|
|
|
struct task_struct *current = scheduler_get_current_process();
|
|
struct timer_list *timer;
|
|
|
|
// If there is already a timer running
|
|
int result = 0;
|
|
if (current->real_timer != NULL) {
|
|
del_timer(current->real_timer);
|
|
result = (current->real_timer->expires - timer_get_ticks()) / TICKS_PER_SECOND;
|
|
timer = current->real_timer;
|
|
|
|
// Returns only the amount of seconds remaining
|
|
if (seconds == 0) {
|
|
kfree(current->real_timer);
|
|
current->real_timer = NULL;
|
|
return result;
|
|
}
|
|
} else {
|
|
if (seconds == 0)
|
|
return 0;
|
|
|
|
// Allocate new timer
|
|
timer = (struct timer_list *)kmalloc(sizeof(struct timer_list));
|
|
}
|
|
|
|
current->real_timer = timer;
|
|
init_timer(timer);
|
|
|
|
timer->expires = timer_get_ticks() + TICKS_PER_SECOND * seconds;
|
|
timer->function = &alarm_timeout;
|
|
timer->data = current->pid;
|
|
|
|
add_timer(timer);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void calc_itimerval(unsigned long incr, unsigned long value, struct itimerval *result)
|
|
{
|
|
result->it_interval.tv_sec = incr / TICKS_PER_SECOND;
|
|
result->it_interval.tv_usec = incr / TICKS_PER_SECOND * 1000;
|
|
|
|
result->it_value.tv_sec = value / TICKS_PER_SECOND;
|
|
result->it_value.tv_usec = value / TICKS_PER_SECOND * 1000;
|
|
}
|
|
|
|
static void update_task_itimerval(int which, const struct itimerval *val)
|
|
{
|
|
unsigned long interval_ticks = val->it_interval.tv_sec * TICKS_PER_SECOND;
|
|
interval_ticks += val->it_interval.tv_usec * TICKS_PER_SECOND / 1000;
|
|
|
|
unsigned long value_ticks = val->it_value.tv_sec * TICKS_PER_SECOND;
|
|
value_ticks += val->it_value.tv_usec * TICKS_PER_SECOND / 1000;
|
|
|
|
struct task_struct *curr = scheduler_get_current_process();
|
|
switch (which) {
|
|
case ITIMER_REAL:
|
|
curr->it_real_incr = interval_ticks;
|
|
curr->it_real_value = value_ticks;
|
|
break;
|
|
|
|
case ITIMER_VIRTUAL:
|
|
curr->it_virt_incr = interval_ticks;
|
|
curr->it_virt_value = value_ticks;
|
|
break;
|
|
|
|
case ITIMER_PROF:
|
|
curr->it_prof_incr = interval_ticks;
|
|
curr->it_prof_value = value_ticks;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int sys_getitimer(int which, struct itimerval *curr_value)
|
|
{
|
|
// Invalid time domain
|
|
if (which < 0 || which > 3)
|
|
return EINVAL;
|
|
|
|
struct task_struct *curr = scheduler_get_current_process();
|
|
switch (which) {
|
|
case ITIMER_REAL: {
|
|
// Extract remaining time in dynamic timer
|
|
unsigned long value = curr->real_timer->expires - timer_get_ticks();
|
|
curr->it_real_value = value;
|
|
calc_itimerval(curr->it_real_incr, curr->it_real_value, curr_value);
|
|
} break;
|
|
case ITIMER_VIRTUAL:
|
|
calc_itimerval(curr->it_virt_incr, curr->it_virt_value, curr_value);
|
|
break;
|
|
case ITIMER_PROF:
|
|
calc_itimerval(curr->it_prof_incr, curr->it_prof_value, curr_value);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Real timer interval timemout
|
|
static void it_real_fn(unsigned long pid)
|
|
{
|
|
struct task_struct *cur = scheduler_get_running_process(pid);
|
|
sys_kill(pid, SIGALRM);
|
|
|
|
// If the real incr is not 0 then restart
|
|
if (cur->it_real_incr != 0) {
|
|
// Create new timer for process
|
|
struct timer_list *real_timer = (struct timer_list *)kmalloc(sizeof(struct timer_list));
|
|
cur->real_timer = real_timer;
|
|
init_timer(real_timer);
|
|
|
|
real_timer->expires = timer_get_ticks() + cur->it_real_incr;
|
|
real_timer->function = &it_real_fn;
|
|
real_timer->data = cur->pid;
|
|
|
|
add_timer(real_timer);
|
|
return;
|
|
}
|
|
|
|
// No more timer
|
|
cur->real_timer = NULL;
|
|
}
|
|
|
|
int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value)
|
|
{
|
|
// Invalid time domain
|
|
if (which < 0 || which > 3)
|
|
return EINVAL;
|
|
|
|
// Returns old timer interval
|
|
if (old_value != NULL)
|
|
sys_getitimer(which, old_value);
|
|
|
|
// Get ticks of interval
|
|
unsigned long interval_ticks = new_value->it_interval.tv_sec * TICKS_PER_SECOND;
|
|
interval_ticks += new_value->it_interval.tv_usec * TICKS_PER_SECOND / 1000;
|
|
|
|
// If interval is 0 removes timer
|
|
struct task_struct *cur = scheduler_get_current_process();
|
|
if (interval_ticks == 0) {
|
|
// Removes real_timer
|
|
if (which == ITIMER_REAL && cur->real_timer != NULL)
|
|
cur->real_timer = NULL;
|
|
|
|
update_task_itimerval(which, new_value);
|
|
return -1;
|
|
}
|
|
|
|
switch (which) {
|
|
// Uses Dynamic Timers
|
|
case ITIMER_REAL: {
|
|
// Remove real_timer if already in use
|
|
struct timer_list *timer = cur->real_timer;
|
|
if (timer != NULL) {
|
|
del_timer(timer); // Recycle memory
|
|
} else {
|
|
// Alloc new timer
|
|
timer = (struct timer_list *)kmalloc(sizeof(struct timer_list));
|
|
}
|
|
|
|
init_timer(timer);
|
|
|
|
timer->expires = timer_get_ticks() + interval_ticks;
|
|
timer->function = &it_real_fn;
|
|
timer->data = cur->pid;
|
|
|
|
add_timer(timer);
|
|
|
|
} break;
|
|
|
|
case ITIMER_VIRTUAL:
|
|
case ITIMER_PROF:
|
|
break;
|
|
}
|
|
|
|
update_task_itimerval(which, new_value);
|
|
return -1;
|
|
}
|
|
|
|
void update_process_profiling_timer(task_struct *proc)
|
|
{
|
|
// If the timer is active
|
|
if (proc->it_prof_incr != 0) {
|
|
proc->it_prof_value += proc->se.exec_runtime;
|
|
if (proc->it_prof_value >= proc->it_prof_incr) {
|
|
sys_kill(proc->pid, SIGPROF);
|
|
proc->it_prof_value = 0;
|
|
}
|
|
}
|
|
} |