From: Tulio A M Mendes Date: Sat, 14 Feb 2026 07:58:29 +0000 (-0300) Subject: feat: increase timer frequency to 100Hz (like Linux) X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=df7c93c9146bc3399f01803bdbdbafe3b78b14f0;p=AdrOS.git feat: increase timer frequency to 100Hz (like Linux) - Central TIMER_HZ=100 and TIMER_MS_PER_TICK=10 constants in timer.h - Replace all hardcoded 50Hz / 20ms-per-tick assumptions: syscall.c (nanosleep, clock_gettime), procfs.c (/proc/uptime), net_ping.c (sleep/time calculations), sys_arch.c (sys_now), sync.c (ksem_wait_timeout ms-to-ticks conversion) - Use timer_init(TIMER_HZ) instead of hardcoded 50 - BSP-only timer tick via lapic_get_id() — APs return early to eliminate sched_lock/vga_lock contention at higher tick rates - vga_flush() skips spinlock + cursor update when nothing dirty - Preempt every 2nd tick (effective 50Hz preemption) to avoid excessive CR3 reloads in emulated environments (QEMU TLB flush overhead). Sleep/timing resolution remains at full 100Hz via process_wake_check on every tick. Tests: 20/20 smoke (11s), 16/16 battery, cppcheck clean --- diff --git a/include/timer.h b/include/timer.h index be9471a..89583df 100644 --- a/include/timer.h +++ b/include/timer.h @@ -3,6 +3,9 @@ #include +#define TIMER_HZ 100 +#define TIMER_MS_PER_TICK (1000 / TIMER_HZ) /* 10 ms */ + void timer_init(uint32_t frequency); uint32_t get_tick_count(void); diff --git a/src/drivers/timer.c b/src/drivers/timer.c index 12137e2..d91fc6b 100644 --- a/src/drivers/timer.c +++ b/src/drivers/timer.c @@ -6,6 +6,10 @@ #include "hal/timer.h" +#if defined(__i386__) +#include "arch/x86/lapic.h" +#endif + static uint32_t tick = 0; uint32_t get_tick_count(void) { @@ -13,11 +17,19 @@ uint32_t get_tick_count(void) { } static void hal_tick_bridge(void) { +#if defined(__i386__) + if (lapic_is_enabled() && lapic_get_id() != 0) return; +#endif tick++; vdso_update_tick(tick); vga_flush(); process_wake_check(tick); - schedule(); + /* Preempt every SCHED_DIVISOR ticks to reduce context-switch + * overhead in emulated environments (QEMU TLB flush on CR3 + * reload is expensive). Sleeping processes still wake at full + * TIMER_HZ resolution via process_wake_check above. */ + if (tick % 2 == 0) + schedule(); } void timer_init(uint32_t frequency) { diff --git a/src/drivers/vga_console.c b/src/drivers/vga_console.c index 52e4cf7..d3c71b5 100644 --- a/src/drivers/vga_console.c +++ b/src/drivers/vga_console.c @@ -208,6 +208,11 @@ void vga_print(const char* str) { } void vga_flush(void) { + /* Quick unlocked check: if nothing is dirty, skip entirely. + * All write paths (vga_write_buf, vga_put_char, vga_print) already + * flush immediately, so this timer-tick path is just a safety net. */ + if (dirty_lo > dirty_hi) return; + uintptr_t flags = spin_lock_irqsave(&vga_lock); vga_flush_to_hw(); spin_unlock_irqrestore(&vga_lock, flags); diff --git a/src/kernel/main.c b/src/kernel/main.c index bdd8ad3..ff80841 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -74,8 +74,8 @@ void kernel_main(const struct boot_info* bi) { vdso_init(); } - // 8. Start Timer (Preemption!) - 50Hz - timer_init(50); + // 8. Start Timer (Preemption!) - 100Hz (like Linux CONFIG_HZ=100) + timer_init(TIMER_HZ); hal_cpu_enable_interrupts(); diff --git a/src/kernel/procfs.c b/src/kernel/procfs.c index de6b0ec..7af3604 100644 --- a/src/kernel/procfs.c +++ b/src/kernel/procfs.c @@ -104,8 +104,8 @@ static uint32_t proc_cmdline_read(fs_node_t* node, uint32_t offset, uint32_t siz static uint32_t proc_uptime_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) { (void)node; uint32_t ticks = get_tick_count(); - uint32_t secs = (ticks * 20) / 1000; - uint32_t frac = ((ticks * 20) % 1000) / 10; + uint32_t secs = (ticks * TIMER_MS_PER_TICK) / 1000; + uint32_t frac = ((ticks * TIMER_MS_PER_TICK) % 1000) / 10; char tmp[64]; uint32_t len = 0; diff --git a/src/kernel/sync.c b/src/kernel/sync.c index 6eb78d2..526ab64 100644 --- a/src/kernel/sync.c +++ b/src/kernel/sync.c @@ -1,6 +1,7 @@ #include "sync.h" #include "process.h" #include "utils.h" +#include "timer.h" extern uint32_t get_tick_count(void); extern void schedule(void); @@ -43,10 +44,10 @@ int ksem_wait_timeout(ksem_t* s, uint32_t timeout_ms) { s->waiters[s->nwaiters++] = current_process; current_process->state = PROCESS_BLOCKED; - /* Set a wake timeout if requested (convert ms to ticks at 50 Hz) */ + /* Set a wake timeout if requested (convert ms to ticks) */ uint32_t deadline = 0; if (timeout_ms > 0) { - uint32_t ticks = (timeout_ms + 19) / 20; /* round up */ + uint32_t ticks = (timeout_ms + TIMER_MS_PER_TICK - 1) / TIMER_MS_PER_TICK; deadline = get_tick_count() + ticks; current_process->wake_at_tick = deadline; current_process->state = PROCESS_SLEEPING; /* timer will wake us */ diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index c7b5738..46c7aa0 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -18,9 +18,9 @@ #include "elf.h" #include "stat.h" +#include "timer.h" #include "vmm.h" #include "pmm.h" -#include "timer.h" #include "hal/mm.h" #include "hal/cpu.h" @@ -49,8 +49,8 @@ struct k_itimerval { #define ITIMER_REAL 0 #define ITIMER_VIRTUAL 1 #define ITIMER_PROF 2 -#define TICKS_PER_SEC 50 -#define USEC_PER_TICK (1000000U / TICKS_PER_SEC) /* 20000 */ +#define TICKS_PER_SEC TIMER_HZ +#define USEC_PER_TICK (1000000U / TICKS_PER_SEC) static uint32_t timeval_to_ticks(const struct k_timeval* tv) { return tv->tv_sec * TICKS_PER_SEC + tv->tv_usec / USEC_PER_TICK; @@ -1705,9 +1705,8 @@ static int syscall_nanosleep_impl(const struct timespec* user_req, struct timesp if (req.tv_nsec >= 1000000000U) return -EINVAL; - const uint32_t TICK_MS = 20; uint32_t ms = req.tv_sec * 1000U + req.tv_nsec / 1000000U; - uint32_t ticks = (ms + TICK_MS - 1) / TICK_MS; + uint32_t ticks = (ms + TIMER_MS_PER_TICK - 1) / TIMER_MS_PER_TICK; if (ticks == 0 && (req.tv_sec > 0 || req.tv_nsec > 0)) ticks = 1; if (ticks > 0) { @@ -1736,8 +1735,7 @@ static int syscall_clock_gettime_impl(uint32_t clk_id, struct timespec* user_tp) tp.tv_nsec = 0; } else { uint32_t ticks = get_tick_count(); - const uint32_t TICK_MS = 20; - uint32_t total_ms = ticks * TICK_MS; + uint32_t total_ms = ticks * TIMER_MS_PER_TICK; tp.tv_sec = total_ms / 1000U; tp.tv_nsec = (total_ms % 1000U) * 1000000U; } diff --git a/src/net/lwip_port/sys_arch.c b/src/net/lwip_port/sys_arch.c index f91f767..c73661e 100644 --- a/src/net/lwip_port/sys_arch.c +++ b/src/net/lwip_port/sys_arch.c @@ -11,6 +11,7 @@ #include "sync.h" #include "process.h" #include "spinlock.h" +#include "timer.h" #include @@ -19,9 +20,9 @@ extern void* kmalloc(uint32_t size); extern void kfree(void* ptr); extern struct process* process_create_kernel(void (*entry)(void)); -/* Return milliseconds since boot. Timer runs at 50 Hz → 20 ms per tick. */ +/* Return milliseconds since boot. */ u32_t sys_now(void) { - return (u32_t)(get_tick_count() * 20); + return (u32_t)(get_tick_count() * TIMER_MS_PER_TICK); } /* ------------------------------------------------------------------ */ diff --git a/src/net/net_ping.c b/src/net/net_ping.c index 08284f1..a5ae9c8 100644 --- a/src/net/net_ping.c +++ b/src/net/net_ping.c @@ -20,6 +20,7 @@ #include "process.h" #include "net.h" #include "e1000.h" +#include "timer.h" #include #include @@ -135,7 +136,7 @@ void net_ping_test(void) { } /* Wait for the E1000 link to stabilize in QEMU */ - process_sleep(100); /* ~2 seconds at 50 Hz */ + process_sleep(2 * TIMER_HZ); /* ~2 seconds */ ip_addr_t target; IP4_ADDR(&target, 10, 0, 2, 2); @@ -168,7 +169,7 @@ void net_ping_test(void) { process_sleep(1); /* yield for 1 tick */ } - uint32_t dt = (get_tick_count() - t0) * 20; + uint32_t dt = (get_tick_count() - t0) * TIMER_MS_PER_TICK; if (ping_got_reply) { kprintf("[PING] reply from 10.0.2.2: seq=%d time=%dms\n", @@ -179,7 +180,7 @@ void net_ping_test(void) { } if (i + 1 < PING_COUNT) - process_sleep(50); /* ~1 second between pings */ + process_sleep(TIMER_HZ); /* ~1 second between pings */ } /* Cleanup in tcpip thread */