From cacd070d24879eb84415670da862e997ed8e5453 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 6 Oct 2015 13:48:25 +0200 Subject: [PATCH] enable the support of dynamic ticks - a dynamic oneshoot timer is used to wakeup the system instead of a periodic timer --- hermit/Makefile | 1 + hermit/TODO.md | 1 - hermit/arch/x86/include/asm/apic.h | 23 +++---- hermit/arch/x86/include/asm/irq.h | 10 +-- hermit/arch/x86/include/asm/processor.h | 4 -- hermit/arch/x86/kernel/apic.c | 21 ++++-- hermit/arch/x86/kernel/entry.asm | 18 ++++-- hermit/arch/x86/kernel/irq.c | 27 ++++---- hermit/arch/x86/kernel/timer.c | 85 +++++++++---------------- hermit/include/hermit/config.h | 3 +- hermit/include/hermit/tasks.h | 4 ++ hermit/include/hermit/time.h | 15 ++++- hermit/kernel/main.c | 8 +-- hermit/kernel/tasks.c | 36 +++++++++-- 14 files changed, 142 insertions(+), 114 deletions(-) diff --git a/hermit/Makefile b/hermit/Makefile index f24e26c4d..efcef1917 100644 --- a/hermit/Makefile +++ b/hermit/Makefile @@ -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] $@ diff --git a/hermit/TODO.md b/hermit/TODO.md index 1283cc81d..302939b1a 100644 --- a/hermit/TODO.md +++ b/hermit/TODO.md @@ -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 diff --git a/hermit/arch/x86/include/asm/apic.h b/hermit/arch/x86/include/asm/apic.h index 687e3d404..4dedc588f 100644 --- a/hermit/arch/x86/include/asm/apic.h +++ b/hermit/arch/x86/include/asm/apic.h @@ -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); diff --git a/hermit/arch/x86/include/asm/irq.h b/hermit/arch/x86/include/asm/irq.h index c703cc1ae..fa4fe612c 100644 --- a/hermit/arch/x86/include/asm/irq.h +++ b/hermit/arch/x86/include/asm/irq.h @@ -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 } diff --git a/hermit/arch/x86/include/asm/processor.h b/hermit/arch/x86/include/asm/processor.h index bf773011e..c6803b16c 100644 --- a/hermit/arch/x86/include/asm/processor.h +++ b/hermit/arch/x86/include/asm/processor.h @@ -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 * diff --git a/hermit/arch/x86/kernel/apic.c b/hermit/arch/x86/kernel/apic.c index f178ee153..97b937701 100644 --- a/hermit/arch/x86/kernel/apic.c +++ b/hermit/arch/x86/kernel/apic.c @@ -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; } diff --git a/hermit/arch/x86/kernel/entry.asm b/hermit/arch/x86/kernel/entry.asm index 4adbdb5c9..70648e3ac 100644 --- a/hermit/arch/x86/kernel/entry.asm +++ b/hermit/arch/x86/kernel/entry.asm @@ -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 diff --git a/hermit/arch/x86/kernel/irq.c b/hermit/arch/x86/kernel/irq.c index 837938af0..b5e87b216 100644 --- a/hermit/arch/x86/kernel/irq.c +++ b/hermit/arch/x86/kernel/irq.c @@ -43,9 +43,9 @@ #include #include -/* +/* * 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) diff --git a/hermit/arch/x86/kernel/timer.c b/hermit/arch/x86/kernel/timer.c index dfa5859cf..f74724ae9 100644 --- a/hermit/arch/x86/kernel/timer.c +++ b/hermit/arch/x86/kernel/timer.c @@ -36,55 +36,28 @@ #include #include -/* +/* * 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; diff --git a/hermit/include/hermit/config.h b/hermit/include/hermit/config.h index c7dcac54d..236b5d0ff 100644 --- a/hermit/include/hermit/config.h +++ b/hermit/include/hermit/config.h @@ -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 diff --git a/hermit/include/hermit/tasks.h b/hermit/include/hermit/tasks.h index 2e3948ac7..492298f7d 100644 --- a/hermit/include/hermit/tasks.h +++ b/hermit/include/hermit/tasks.h @@ -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) diff --git a/hermit/include/hermit/time.h b/hermit/include/hermit/time.h index 99e6dd7cd..c599cef7f 100644 --- a/hermit/include/hermit/time.h +++ b/hermit/include/hermit/time.h @@ -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 + #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 diff --git a/hermit/kernel/main.c b/hermit/kernel/main.c index 89cefe202..e54b0719a 100644 --- a/hermit/kernel/main.c +++ b/hermit/kernel/main.c @@ -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 */ diff --git a/hermit/kernel/tasks.c b/hermit/kernel/tasks.c index c759ab5d4..03dd4efbe 100644 --- a/hermit/kernel/tasks.c +++ b/hermit/kernel/tasks.c @@ -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