2010-07-31 15:53:30 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2010 Stefan Lankes, Chair for Operating Systems,
|
|
|
|
* RWTH Aachen University
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*
|
|
|
|
* This file is part of MetalSVM.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <metalsvm/stdio.h>
|
|
|
|
#include <metalsvm/string.h>
|
|
|
|
#include <metalsvm/tasks.h>
|
|
|
|
#include <metalsvm/time.h>
|
|
|
|
#include <metalsvm/processor.h>
|
2011-04-21 10:13:58 +02:00
|
|
|
#include <metalsvm/errno.h>
|
2011-08-03 21:41:06 +02:00
|
|
|
#include <metalsvm/spinlock.h>
|
2010-08-04 17:22:58 +00:00
|
|
|
#include <asm/irq.h>
|
2010-08-03 10:20:32 +00:00
|
|
|
#include <asm/irqflags.h>
|
2010-09-10 22:18:55 +00:00
|
|
|
#include <asm/gdt.h>
|
|
|
|
#include <asm/tss.h>
|
2010-08-05 11:53:02 +00:00
|
|
|
#include <asm/vga.h>
|
2010-07-31 15:53:30 +00:00
|
|
|
|
|
|
|
/*
|
2010-10-25 16:58:31 +00:00
|
|
|
* This will keep track of how many ticks the system
|
2010-07-31 15:53:30 +00:00
|
|
|
* has been running for
|
|
|
|
*/
|
2011-08-03 21:41:06 +02:00
|
|
|
static volatile uint64_t timer_ticks = 0;
|
2010-07-31 15:53:30 +00:00
|
|
|
|
2011-08-18 12:16:31 +02:00
|
|
|
#if MAX_CORES > 1
|
|
|
|
extern atomic_int32_t cpu_online;
|
|
|
|
#endif
|
2012-07-03 16:46:54 +02:00
|
|
|
static int8_t use_tickless = 0;
|
|
|
|
static uint64_t last_rdtsc = 0;
|
2011-08-18 12:16:31 +02:00
|
|
|
|
2010-11-04 20:15:39 +00:00
|
|
|
uint64_t get_clock_tick(void)
|
|
|
|
{
|
|
|
|
return timer_ticks;
|
|
|
|
}
|
|
|
|
|
2012-07-03 16:46:54 +02:00
|
|
|
void start_tickless(void)
|
|
|
|
{
|
|
|
|
use_tickless = 1;
|
2012-07-19 08:28:23 +02:00
|
|
|
rmb();
|
2012-07-03 16:46:54 +02:00
|
|
|
last_rdtsc = rdtsc();
|
|
|
|
}
|
|
|
|
|
|
|
|
void end_tickless(void)
|
|
|
|
{
|
|
|
|
use_tickless = 0;
|
|
|
|
last_rdtsc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void check_ticks(void)
|
|
|
|
{
|
|
|
|
if (!use_tickless)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#if MAX_CORES > 1
|
|
|
|
if (smp_id() == 0)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
uint64_t curr_rdtsc = rdtsc();
|
|
|
|
|
2012-07-19 08:28:23 +02:00
|
|
|
rmb();
|
2012-07-03 16:46:54 +02:00
|
|
|
if (curr_rdtsc - last_rdtsc > 1000000ULL*(uint64_t)get_cpu_frequency() / (uint64_t)TIMER_FREQ) {
|
|
|
|
timer_ticks++;
|
|
|
|
last_rdtsc = curr_rdtsc;
|
2012-07-19 08:28:23 +02:00
|
|
|
rmb();
|
2012-07-03 16:46:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-21 10:13:58 +02:00
|
|
|
int sys_times(struct tms* buffer, clock_t* clock)
|
|
|
|
{
|
|
|
|
if (BUILTIN_EXPECT(!buffer, 0))
|
|
|
|
return -EINVAL;
|
|
|
|
if (BUILTIN_EXPECT(!clock, 0))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memset(buffer, 0x00, sizeof(struct tms));
|
|
|
|
*clock = buffer->tms_utime = (clock_t) ((timer_ticks - per_core(current_task)->start_tick) * CLOCKS_PER_SEC / TIMER_FREQ);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-31 15:53:30 +00:00
|
|
|
/*
|
|
|
|
* Handles the timer. In this case, it's very simple: We
|
|
|
|
* increment the 'timer_ticks' variable every time the
|
|
|
|
* timer fires.
|
|
|
|
*/
|
2010-08-09 11:47:51 +00:00
|
|
|
static void timer_handler(struct state *s)
|
2010-07-31 15:53:30 +00:00
|
|
|
{
|
|
|
|
/* Increment our 'tick counter' */
|
2011-07-18 09:10:23 +02:00
|
|
|
#if MAX_CORES > 1
|
|
|
|
if (smp_id() == 0)
|
|
|
|
#endif
|
2011-08-03 21:41:06 +02:00
|
|
|
{
|
|
|
|
timer_ticks++;
|
|
|
|
|
2011-08-06 15:55:34 +02:00
|
|
|
/*
|
|
|
|
* Every TIMER_FREQ clocks (approximately 1 second), we will
|
|
|
|
* display a message on the screen
|
|
|
|
*/
|
|
|
|
/*if (timer_ticks % TIMER_FREQ == 0) {
|
|
|
|
vga_puts("One second has passed\n");
|
|
|
|
}*/
|
2011-09-22 10:58:03 +02:00
|
|
|
|
|
|
|
/* Dump load every minute */
|
2011-09-22 21:37:57 +02:00
|
|
|
//if (timer_ticks % (TIMER_FREQ*60) == 0)
|
|
|
|
// dump_load();
|
2011-08-03 21:41:06 +02:00
|
|
|
}
|
2011-08-18 12:16:31 +02:00
|
|
|
|
2012-07-03 16:46:54 +02:00
|
|
|
#ifndef CONFIG_TICKLESS
|
2011-08-18 12:16:31 +02:00
|
|
|
update_load();
|
|
|
|
|
|
|
|
#if MAX_CORES > 1
|
2011-09-22 10:58:03 +02:00
|
|
|
if (atomic_int32_read(&cpu_online) > 1)
|
2011-08-18 12:16:31 +02:00
|
|
|
load_balancing();
|
|
|
|
#endif
|
2012-07-03 16:46:54 +02:00
|
|
|
#endif
|
2010-07-31 15:53:30 +00:00
|
|
|
}
|
|
|
|
|
2011-08-06 15:55:34 +02:00
|
|
|
int timer_wait(unsigned int ticks)
|
2010-07-31 15:53:30 +00:00
|
|
|
{
|
|
|
|
uint64_t eticks = timer_ticks + ticks;
|
2011-08-17 23:32:39 -07:00
|
|
|
|
2011-08-03 21:41:06 +02:00
|
|
|
task_t* curr_task = per_core(current_task);
|
|
|
|
|
2011-08-06 15:55:34 +02:00
|
|
|
if (curr_task->status == TASK_IDLE)
|
2011-08-03 21:41:06 +02:00
|
|
|
{
|
2011-08-06 15:55:34 +02:00
|
|
|
/*
|
|
|
|
* This will continuously loop until the given time has
|
|
|
|
* been reached
|
|
|
|
*/
|
2011-08-03 21:41:06 +02:00
|
|
|
while (timer_ticks < eticks) {
|
|
|
|
check_workqueues();
|
|
|
|
|
|
|
|
// recheck break condition
|
|
|
|
if (timer_ticks >= eticks)
|
|
|
|
break;
|
|
|
|
|
|
|
|
HALT;
|
|
|
|
}
|
|
|
|
} else if (timer_ticks < eticks) {
|
2011-05-17 08:13:20 -07:00
|
|
|
check_workqueues();
|
|
|
|
|
2011-08-03 21:41:06 +02:00
|
|
|
if (timer_ticks < eticks) {
|
2011-08-17 13:51:19 +02:00
|
|
|
set_timer(eticks);
|
2011-08-03 21:41:06 +02:00
|
|
|
reschedule();
|
2011-08-06 15:55:34 +02:00
|
|
|
}
|
2011-05-17 08:13:20 -07:00
|
|
|
}
|
2011-08-06 15:55:34 +02:00
|
|
|
|
|
|
|
return 0;
|
2010-07-31 15:53:30 +00:00
|
|
|
}
|
|
|
|
|
2010-10-25 16:58:31 +00:00
|
|
|
#define LATCH(f) ((CLOCK_TICK_RATE + f/2) / f)
|
2011-12-15 18:34:27 +01:00
|
|
|
#define WAIT_SOME_TIME() do { uint64_t start = rdtsc(); \
|
|
|
|
while(rdtsc() - start < 1000000) ; \
|
|
|
|
} while (0)
|
2010-10-25 16:58:31 +00:00
|
|
|
|
2010-07-31 15:53:30 +00:00
|
|
|
/*
|
|
|
|
* Sets up the system clock by installing the timer handler
|
|
|
|
* into IRQ0
|
|
|
|
*/
|
|
|
|
int timer_init(void)
|
|
|
|
{
|
2010-11-04 20:15:39 +00:00
|
|
|
/*
|
2010-11-29 02:39:10 +00:00
|
|
|
* Installs 'timer_handler' for the PIC and APIC timer,
|
2010-11-04 20:15:39 +00:00
|
|
|
* only one handler will be later used.
|
|
|
|
*/
|
2011-07-21 09:59:29 +02:00
|
|
|
irq_install_handler(32, timer_handler);
|
2010-12-10 06:16:58 +00:00
|
|
|
irq_install_handler(123, timer_handler);
|
2010-07-31 15:53:30 +00:00
|
|
|
|
2010-11-26 05:33:02 +00:00
|
|
|
/*
|
|
|
|
* The Rock Creek processor doesn't posseess an tradional PIC.
|
|
|
|
* Therefore, we have not to configure the PIC timer.
|
|
|
|
*/
|
|
|
|
#ifndef CONFIG_ROCKCREEK
|
2011-12-15 18:34:27 +01:00
|
|
|
/*
|
|
|
|
* Port 0x43 is for initializing the PIT:
|
|
|
|
*
|
|
|
|
* 0x34 means the following:
|
|
|
|
* 0b... (step-by-step binary representation)
|
|
|
|
* ... 00 - channel 0
|
|
|
|
* ... 11 - write two values to counter register:
|
|
|
|
* first low-, then high-byte
|
|
|
|
* ... 010 - mode number 2: "rate generator" / frequency divider
|
|
|
|
* ... 0 - binary counter (the alternative is BCD)
|
|
|
|
*/
|
2010-11-04 20:15:39 +00:00
|
|
|
outportb(0x43, 0x34);
|
2011-12-15 18:34:27 +01:00
|
|
|
|
|
|
|
WAIT_SOME_TIME();
|
|
|
|
|
|
|
|
/* Port 0x40 is for the counter register of channel 0 */
|
|
|
|
|
2010-11-04 20:15:39 +00:00
|
|
|
outportb(0x40, LATCH(TIMER_FREQ) & 0xFF); /* low byte */
|
2011-12-15 18:34:27 +01:00
|
|
|
|
|
|
|
WAIT_SOME_TIME();
|
|
|
|
|
2010-11-04 20:15:39 +00:00
|
|
|
outportb(0x40, LATCH(TIMER_FREQ) >> 8); /* high byte */
|
2010-11-26 05:33:02 +00:00
|
|
|
#endif
|
2010-07-31 15:53:30 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|