/* * 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 #include #include #include #include #include #include #include #include #include #include #include /* * This will keep track of how many ticks the system * has been running for */ static volatile uint64_t timer_ticks = 0; #if MAX_CORES > 1 extern atomic_int32_t cpu_online; #endif static int8_t use_tickless = 0; static uint64_t last_rdtsc = 0; uint64_t get_clock_tick(void) { return timer_ticks; } void start_tickless(void) { use_tickless = 1; rmb(); 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(); rmb(); if (curr_rdtsc - last_rdtsc > 1000000ULL*(uint64_t)get_cpu_frequency() / (uint64_t)TIMER_FREQ) { timer_ticks++; last_rdtsc = curr_rdtsc; rmb(); } } } 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; } /* * Handles the timer. In this case, it's very simple: We * increment the 'timer_ticks' variable every time the * timer fires. */ static void timer_handler(struct state *s) { /* Increment our 'tick counter' */ #if MAX_CORES > 1 if (smp_id() == 0) #endif { timer_ticks++; /* * 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"); }*/ /* Dump load every minute */ //if (timer_ticks % (TIMER_FREQ*60) == 0) // dump_load(); } #ifndef CONFIG_TICKLESS update_load(); #if MAX_CORES > 1 if (atomic_int32_read(&cpu_online) > 1) load_balancing(); #endif #endif } int timer_wait(unsigned int ticks) { uint64_t eticks = timer_ticks + ticks; task_t* curr_task = per_core(current_task); if (curr_task->status == TASK_IDLE) { /* * This will continuously loop until the given time has * been reached */ while (timer_ticks < eticks) { check_workqueues(); // recheck break condition if (timer_ticks >= eticks) break; HALT; } } else if (timer_ticks < eticks) { check_workqueues(); if (timer_ticks < eticks) { set_timer(eticks); reschedule(); } } return 0; } #define LATCH(f) ((CLOCK_TICK_RATE + f/2) / f) #define WAIT_SOME_TIME() do { uint64_t start = rdtsc(); \ while(rdtsc() - start < 1000000) ; \ } while (0) /* * Sets up the system clock by installing the timer handler * into IRQ0 */ int timer_init(void) { /* * Installs 'timer_handler' for the PIC and APIC timer, * only one handler will be later used. */ irq_install_handler(32, timer_handler); irq_install_handler(123, timer_handler); /* * The Rock Creek processor doesn't posseess an tradional PIC. * Therefore, we have not to configure the PIC timer. */ #ifndef CONFIG_ROCKCREEK /* * 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) */ outportb(0x43, 0x34); WAIT_SOME_TIME(); /* Port 0x40 is for the counter register of channel 0 */ outportb(0x40, LATCH(TIMER_FREQ) & 0xFF); /* low byte */ WAIT_SOME_TIME(); outportb(0x40, LATCH(TIMER_FREQ) >> 8); /* high byte */ #endif return 0; }