1
0
Fork 0
mirror of https://github.com/hermitcore/libhermit.git synced 2025-03-09 00:00:03 +01:00

enable the support of dynamic ticks

- a dynamic oneshoot timer is used to wakeup the system instead of
  a periodic timer
This commit is contained in:
Stefan Lankes 2015-10-06 13:48:25 +02:00
parent 088b496baf
commit cacd070d24
14 changed files with 142 additions and 114 deletions

View file

@ -151,6 +151,7 @@ include/hermit/config.inc: include/hermit/config.h
@awk '/^#define MAX_CORES/{ print "%define MAX_CORES", $$3 }' include/hermit/config.h >> include/hermit/config.inc
@awk '/^#define KERNEL_STACK_SIZE/{ print "%define KERNEL_STACK_SIZE", $$3 }' include/hermit/config.h >> include/hermit/config.inc
@awk '/^#define VIDEO_MEM_ADDR/{ print "%define VIDEO_MEM_ADDR", $$3 }' include/hermit/config.h >> include/hermit/config.inc
@awk '/^#define DYNAMIC_TICKS/{ print "%define DYNAMIC_TICKS", $$3 }' include/hermit/config.h >> include/hermit/config.inc
%.o : %.asm include/hermit/config.inc
@echo [ASM] $@

View file

@ -6,5 +6,4 @@ TODO list
* prioririty handling in the Pthread library is still missing
* use gs register to accelerate the access to newlib's reentrant structure
* revise TLB shoot down
* revise and check tickless support (the tickless support is currently not tested)
* revise of the mmnif driver

View file

@ -55,9 +55,9 @@ extern "C" {
/// LVT Timer Register
#define APIC_LVT_T 0x0320
/// LVT Thermal Sensor Register
#define APIC_LVT_TSR 0x0330
#define APIC_LVT_TSR 0x0330
/// LVT Performance Monitoring Counters Register
#define APIC_LVT_PMC 0x0340
#define APIC_LVT_PMC 0x0340
/// LVT LINT0 Register
#define APIC_LINT0 0x0350
/// LVT LINT1 Register
@ -168,29 +168,29 @@ typedef struct {
struct {
uint32_t vector : 8,
delivery_mode : 3, /* 000: FIXED
* 001: lowest prio
* 111: ExtINT
*/
* 001: lowest prio
* 111: ExtINT
*/
dest_mode : 1, /* 0: physical, 1: logical */
delivery_status : 1,
delivery_status : 1,
polarity : 1,
irr : 1,
irr : 1,
trigger : 1, /* 0: edge, 1: level */
mask : 1, /* 0: enabled, 1: disabled */
__reserved_2 : 15;
__reserved_2 : 15;
} bitfield;
uint32_t whole;
} lower;
union {
struct {
uint32_t __reserved_1 : 24,
physical_dest : 4,
__reserved_2 : 4;
physical_dest : 4,
__reserved_2 : 4;
} physical;
struct {
uint32_t __reserved_1 : 24,
logical_dest : 8;
logical_dest : 8;
} logical;
uint32_t upper;
} dest;
@ -203,6 +203,7 @@ int apic_calibration(void);
int apic_is_enabled(void);
int apic_enable_timer(void);
int apic_disable_timer(void);
int apic_timer_deadline(uint32_t);
int apic_send_ipi(uint64_t dest, uint8_t irq);
int ioapic_inton(uint8_t irq, uint8_t apicid);
int ioapic_intoff(uint8_t irq, uint8_t apicid);

View file

@ -73,17 +73,11 @@ int irq_uninstall_handler(unsigned int irq);
*/
int irq_init(void);
/** @brief Disable the timer interrupt
/** @brief Switch from a fix to a dynamic timer period
*
* @return 0 on success
*/
int disable_timer_irq(void);
/** @brief Enable the timer interrupt
*
* @return 0 on success
*/
int enable_timer_irq(void);
int enable_dynticks(void);
#ifdef __cplusplus
}

View file

@ -622,11 +622,7 @@ static inline size_t lsb(size_t i)
/// The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop.
#define PAUSE asm volatile ("pause")
/// The HALT instruction stops the processor until the next interrupt arrives
#ifndef CONFIG_TICKLESS
#define HALT asm volatile ("hlt")
#else
#define HALT asm volatile("pause")
#endif
/** @brief Init several subsystems
*

View file

@ -48,9 +48,6 @@
*/
extern const void kernel_start;
extern void start_tickless(void);
extern void end_tickless(void);
#define IOAPIC_ADDR ((size_t) &kernel_start - 2*PAGE_SIZE)
#define LAPIC_ADDR ((size_t) &kernel_start - 1*PAGE_SIZE)
#define MAX_APIC_CORES 256
@ -221,13 +218,28 @@ static inline uint32_t apic_lvt_entries(void)
return 0;
}
int apic_timer_deadline(uint32_t t)
{
if (BUILTIN_EXPECT(apic_is_enabled() && icr, 1)) {
//kprintf("timer oneshot %ld\n", t);
// create one shot interrup
lapic_write(APIC_DCR, 0xB); // set it to 1 clock increments
lapic_write(APIC_LVT_T, 0x7B); // connects the timer to 123 and enables it
lapic_write(APIC_ICR, icr*t);
return 0;
}
return -EINVAL;
}
int apic_disable_timer(void)
{
if (BUILTIN_EXPECT(!apic_is_enabled(), 0))
return -EINVAL;
lapic_write(APIC_LVT_T, 0x10000); // disable timer interrupt
start_tickless();
return 0;
}
@ -238,7 +250,6 @@ int apic_enable_timer(void)
lapic_write(APIC_DCR, 0xB); // set it to 1 clock increments
lapic_write(APIC_LVT_T, 0x2007B); // connects the timer to 123 and enables it
lapic_write(APIC_ICR, icr);
end_tickless();
return 0;
}

View file

@ -54,7 +54,6 @@ align 4
global boot_processor
global cpu_online
global possible_cpus
global timer_ticks
global current_boot_id
global isle
global possible_isles
@ -71,7 +70,7 @@ align 4
boot_processor dd -1
cpu_online dd 0
possible_cpus dd 0
timer_ticks dq 0
dq 0 ; reserved for future extensions
current_boot_id dd 0
isle dd -1
image_size dq 0
@ -362,12 +361,19 @@ apic_svr:
push byte 127
jmp common_stub
global wakeup
align 16
wakeup:
push byte 0 ; pseudo error code
push byte 121
jmp common_stub
global mmnif_irq
align 16
mmnif_irq:
push byte 0 ; pseudo error code
push byte 122
jmp common_stub
push byte 0 ; pseudo error code
push byte 122
jmp common_stub
extern irq_handler
extern get_current_stack
@ -413,8 +419,10 @@ isrsyscall:
call [rax*8+syscall_table]
push rax ; result, which we have to return
%ifdef DYNAMIC_TICKS
extern check_ticks
call check_ticks
%endif
extern check_timers
call check_timers

View file

@ -43,9 +43,9 @@
#include <asm/io.h>
#include <asm/apic.h>
/*
/*
* These are our own ISRs that point to our special IRQ handler
* instead of the regular 'fault_handler' function
* instead of the regular 'fault_handler' function
*/
extern void irq0(void);
extern void irq1(void);
@ -78,6 +78,7 @@ extern void apic_lint0(void);
extern void apic_lint1(void);
extern void apic_error(void);
extern void apic_svr(void);
extern void wakeup(void);
extern void mmnif_irq(void);
#define MAX_HANDLERS 256
@ -120,7 +121,7 @@ int irq_uninstall_handler(unsigned int irq)
* actually happening. We send commands to the Programmable
* Interrupt Controller (PICs - also called the 8259's) in
* order to make IRQ0 to 15 be remapped to IDT entries 32 to
* 47
* 47
*/
static int irq_remap(void)
{
@ -138,7 +139,7 @@ static int irq_remap(void)
return 0;
}
int disable_timer_irq(void)
int enable_dynticks(void)
{
if (BUILTIN_EXPECT(apic_is_enabled(), 1))
return apic_disable_timer();
@ -146,6 +147,7 @@ int disable_timer_irq(void)
return -EINVAL;
}
#if 0
int enable_timer_irq(void)
{
if (BUILTIN_EXPECT(apic_is_enabled(), 1))
@ -153,6 +155,7 @@ int enable_timer_irq(void)
return -EINVAL;
}
#endif
/** @brief Remap IRQs and install ISRs in IDT
*
@ -218,6 +221,8 @@ static int irq_install(void)
idt_set_gate(113, (size_t)irq81, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP, 0);
idt_set_gate(121, (size_t)wakeup, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP, 0);
idt_set_gate(122, (size_t)mmnif_irq, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP, 0);
@ -250,10 +255,10 @@ int irq_init(void)
* Each of the IRQ ISRs point to this function, rather than
* the 'fault_handler' in 'isrs.c'. The IRQ Controllers need
* to be told when you are done servicing them, so you need
* to send them an "End of Interrupt" command. If we use the PIC
* instead of the APIC, we have two 8259 chips: The first one
* exists at 0x20, the second one exists at 0xA0. If the second
* controller (an IRQ from 8 to 15) gets an interrupt, you need to
* to send them an "End of Interrupt" command. If we use the PIC
* instead of the APIC, we have two 8259 chips: The first one
* exists at 0x20, the second one exists at 0xA0. If the second
* controller (an IRQ from 8 to 15) gets an interrupt, you need to
* acknowledge the interrupt at BOTH controllers, otherwise, you
* only send an EOI command to the first controller. If you don't send
* an EOI, it won't raise any more IRQs.
@ -270,10 +275,10 @@ size_t** irq_handler(struct state *s)
check_workqueues_in_irqhandler(s->int_no);
/*
/*
* Find out if we have a custom handler to run for this
* IRQ and then finally, run it
*/
* IRQ and then finally, run it
*/
if (BUILTIN_EXPECT(s->int_no < MAX_HANDLERS, 1)) {
handler = irq_routines[s->int_no];
if (handler)

View file

@ -36,55 +36,28 @@
#include <asm/irqflags.h>
#include <asm/io.h>
/*
/*
* This will keep track of how many ticks the system
* has been running for
* has been running for
*/
extern volatile uint64_t timer_ticks;
DEFINE_PER_CORE(uint64_t, timer_ticks, 0);
extern uint32_t cpu_freq;
extern int32_t boot_processor;
static int8_t use_tickless = 0;
static uint64_t last_rdtsc = 0;
#if MAX_CORES > 1
static spinlock_irqsave_t ticks_lock = SPINLOCK_IRQSAVE_INIT;
#endif
void start_tickless(void)
{
use_tickless = 1;
if (has_rdtscp())
last_rdtsc = rdtscp(NULL);
else
last_rdtsc = rdtsc();
rmb();
}
void end_tickless(void)
{
use_tickless = 0;
last_rdtsc = 0;
}
#ifdef DYNAMIC_TICKS
DEFINE_PER_CORE(uint64_t, last_rdtsc, 0);
void check_ticks(void)
{
if (!use_tickless)
return;
#if MAX_CORES > 1
spinlock_irqsave_lock(&ticks_lock);
#endif
if (has_rdtscp()){
uint64_t curr_rdtsc = rdtscp(NULL);
uint64_t diff;
rmb();
diff = ((curr_rdtsc - last_rdtsc) * (uint64_t)TIMER_FREQ) / (1000000ULL*(uint64_t)get_cpu_frequency());
diff = ((curr_rdtsc - per_core(last_rdtsc)) * (uint64_t)TIMER_FREQ) / (1000000ULL*(uint64_t)get_cpu_frequency());
if (diff > 0) {
timer_ticks += diff;
last_rdtsc = curr_rdtsc;
set_per_core(timer_ticks, per_core(timer_ticks) + diff);
set_per_core(last_rdtsc, curr_rdtsc);
rmb();
}
} else {
@ -92,37 +65,31 @@ void check_ticks(void)
uint64_t diff;
rmb();
diff = ((curr_rdtsc - last_rdtsc) * (uint64_t)TIMER_FREQ) / (1000000ULL*(uint64_t)get_cpu_frequency());
diff = ((curr_rdtsc - per_core(last_rdtsc)) * (uint64_t)TIMER_FREQ) / (1000000ULL*(uint64_t)get_cpu_frequency());
if (diff > 0) {
timer_ticks += diff;
last_rdtsc = curr_rdtsc;
set_per_core(timer_ticks, per_core(timer_ticks) + diff);
set_per_core(last_rdtsc, curr_rdtsc);
rmb();
}
}
#if MAX_CORES > 1
spinlock_irqsave_unlock(&ticks_lock);
}
#endif
}
uint64_t get_clock_tick(void)
static void wakeup_handler(struct state *s)
{
return timer_ticks;
}
/*
/*
* Handles the timer. In this case, it's very simple: We
* increment the 'timer_ticks' variable every time the
* timer fires.
* timer fires.
*/
static void timer_handler(struct state *s)
{
#if MAX_CORES > 1
if (CORE_ID == boot_processor)
#ifndef DYNAMIC_TICKS
/* Increment our 'tick counter' */
set_per_core(timer_ticks, per_core(timer_ticks)+1);
#endif
/* Increment our 'tick counter' */
timer_ticks++;
#if 0
/*
@ -138,7 +105,7 @@ static void timer_handler(struct state *s)
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)
@ -173,18 +140,26 @@ int timer_wait(unsigned int ticks)
while(rdtsc() - start < 1000000) ; \
} while (0)
/*
/*
* Sets up the system clock by installing the timer handler
* into IRQ0
* 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);
irq_install_handler(121, wakeup_handler);
#ifdef DYNAMIC_TICKS
if (has_rdtscp())
last_rdtsc = rdtscp(NULL);
else
last_rdtsc = rdtsc();
#endif
if (cpu_freq) // do we need to configure the timer?
return 0;

View file

@ -48,12 +48,11 @@ extern "C" {
#define BYTE_ORDER LITTLE_ENDIAN
//#define CONFIG_TICKLESS
#define DYNAMIC_TICKS
#define BUILTIN_EXPECT(exp, b) __builtin_expect((exp), (b))
//#define BUILTIN_EXPECT(exp, b) (exp)
#define NORETURN __attribute__((noreturn))
#define STDCALL __attribute__((stdcall))
#define HAVE_ARCH_MEMSET
#define HAVE_ARCH_MEMCPY

View file

@ -230,13 +230,17 @@ void NORETURN leave_kernel_task(void);
* */
void check_scheduling(void);
#ifdef DYNAMIC_TICKS
/** @brief check, if the tick counter has to be updated
* */
void check_ticks(void);
#endif
static inline void check_workqueues_in_irqhandler(int irq)
{
#ifdef DYNAMIC_TICKS
check_ticks();
#endif
check_timers();
if (irq < 0)

View file

@ -25,7 +25,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
/**
* @author Stefan Lankes
* @file include/hermit/time.h
* @brief Time related functions
@ -34,6 +34,8 @@
#ifndef __TIME_H__
#define __TIME_H__
#include <asm/apic.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -47,7 +49,7 @@ struct tms {
clock_t tms_cstime;
};
/** @brief Initialize Timer interrupts
/** @brief Initialize Timer interrupts
*
* This procedure installs IRQ handlers for timer interrupts
*/
@ -61,10 +63,15 @@ int timer_init(void);
*/
int timer_wait(unsigned int ticks);
DECLARE_PER_CORE(uint64_t, timer_ticks);
/** @brief Returns the current number of ticks.
* @return Current number of ticks
*/
uint64_t get_clock_tick(void);
static inline uint64_t get_clock_tick(void)
{
return per_core(timer_ticks);
}
/** @brief sleep some seconds
*
@ -74,6 +81,8 @@ uint64_t get_clock_tick(void);
*/
static inline void sleep(unsigned int sec) { timer_wait(sec*TIMER_FREQ); }
static inline int timer_deadline(uint32_t t) { return apic_timer_deadline(t); }
#ifdef __cplusplus
}
#endif

View file

@ -195,8 +195,8 @@ int smp_main(void)
{
int32_t cpu = atomic_int32_inc(&cpu_online);
#ifdef CONFIG_TICKLESS
disable_timer_irq();
#ifdef DYNAMIC_TICKS
enable_dynticks();
#endif
/* wait for the other cpus */
@ -324,8 +324,8 @@ int main(void)
list_fs(fs_root, 1);
#endif
#ifdef CONFIG_TICKLESS
disable_timer_irq();
#ifdef DYNAMIC_TICKS
enable_dynticks();
#endif
/* wait for the other cpus */

View file

@ -79,7 +79,11 @@ void check_scheduling(void)
uint32_t get_highest_priority(void)
{
return msb(readyqueues[CORE_ID].prio_bitmap);
uint32_t prio = msb(readyqueues[CORE_ID].prio_bitmap);
if (prio > MAX_PRIO)
return 0;
return prio;
}
int multitasking_init(void)
@ -350,6 +354,9 @@ out:
if (ret)
kfree(stack);
if (core_id != CORE_ID)
apic_send_ipi(core_id, 121);
return ret;
}
@ -448,6 +455,9 @@ out:
kfree(counter);
}
if (core_id != CORE_ID)
apic_send_ipi(core_id, 121);
return ret;
}
@ -527,6 +537,12 @@ int wakeup_task(tid_t id)
readyqueues[core_id].queue[prio-1].last = task;
}
spinlock_irqsave_unlock(&readyqueues[core_id].lock);
#ifdef DYNAMIC_TICKS
// send IPI to be sure that the scheuler recognize the new task
if (core_id != CORE_ID)
apic_send_ipi(core_id, 121);
#endif
}
irq_nested_enable(flags);
@ -636,6 +652,9 @@ int set_timer(uint64_t deadline)
if (!tmp) {
readyqueues[core_id].timers.first = readyqueues[core_id].timers.last = curr_task;
curr_task->prev = curr_task->next = NULL;
#ifdef DYNAMIC_TICKS
timer_deadline(deadline-get_clock_tick());
#endif
} else {
while(tmp && (deadline >= tmp->timeout))
tmp = tmp->next;
@ -655,8 +674,12 @@ int set_timer(uint64_t deadline)
tmp->prev = curr_task;
if (curr_task->prev)
curr_task->prev->next = curr_task;
if (readyqueues[core_id].timers.first == tmp)
if (readyqueues[core_id].timers.first == tmp) {
readyqueues[core_id].timers.first = curr_task;
#ifdef DYNAMIC_TICKS
timer_deadline(deadline-get_clock_tick());
#endif
}
}
}
@ -684,10 +707,13 @@ void check_timers(void)
// remove timer from queue
readyqueues[core_id].timers.first = readyqueues[core_id].timers.first->next;
if (readyqueues[core_id].timers.first)
if (readyqueues[core_id].timers.first) {
readyqueues[core_id].timers.first->prev = NULL;
else
readyqueues[core_id].timers.last = NULL;
#ifdef DYNAMIC_TICKS
if (readyqueues[core_id].timers.first->timeout > get_clock_tick())
timer_deadline(readyqueues[core_id].timers.first->timeout-current_tick);
#endif
} else readyqueues[core_id].timers.last = NULL;
task->flags &= ~TASK_TIMER;
// wakeup task