diff --git a/arch/wasm/configs/defconfig b/arch/wasm/configs/defconfig index db8184682372f1..6bdeabdc599a48 100644 --- a/arch/wasm/configs/defconfig +++ b/arch/wasm/configs/defconfig @@ -1,4 +1,6 @@ # CONFIG_LOCALVERSION_AUTO is not set +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y # CONFIG_CPU_ISOLATION is not set CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_BZIP2 is not set @@ -46,7 +48,6 @@ CONFIG_HW_RANDOM_VIRTIO=y # CONFIG_VIRTIO_MENU is not set # CONFIG_VHOST_MENU is not set CONFIG_EXT2_FS=y -# CONFIG_FILE_LOCKING is not set # CONFIG_DNOTIFY is not set # CONFIG_INOTIFY_USER is not set # CONFIG_DEBUG_MISC is not set diff --git a/arch/wasm/include/asm/irq.h b/arch/wasm/include/asm/irq.h index c008db55118368..f598205c0d9fa1 100644 --- a/arch/wasm/include/asm/irq.h +++ b/arch/wasm/include/asm/irq.h @@ -2,7 +2,8 @@ #define _WASM_IRQ_H #define IPI_IRQ 1 -#define FIRST_EXT_IRQ 2 +#define TIMER_IRQ 2 +#define FIRST_EXT_IRQ 3 #define NR_IRQS 64 int wasm_alloc_irq(void); diff --git a/arch/wasm/include/asm/sigcontext.h b/arch/wasm/include/asm/sigcontext.h index 7a8a1966f7524d..179a36714178b4 100644 --- a/arch/wasm/include/asm/sigcontext.h +++ b/arch/wasm/include/asm/sigcontext.h @@ -4,6 +4,7 @@ struct pt_regs { long syscall_nr; unsigned long syscall_args[6]; + int user_mode; }; struct sigcontext { diff --git a/arch/wasm/include/uapi/asm/ptrace.h b/arch/wasm/include/uapi/asm/ptrace.h index 59d7f37c44e679..db69065aefa149 100644 --- a/arch/wasm/include/uapi/asm/ptrace.h +++ b/arch/wasm/include/uapi/asm/ptrace.h @@ -6,9 +6,7 @@ struct task_struct; -#define user_mode(regs) (__builtin_trap(),0) -#define kernel_mode(regs) (__builtin_trap(),0) -#define profile_pc(regs) (__builtin_trap(),0) +#define user_mode(regs) (regs->user_mode) #define instruction_pointer(regs) (-1) #define user_stack_pointer(regs) (__builtin_trap(),0) diff --git a/arch/wasm/kernel/irq.c b/arch/wasm/kernel/irq.c index b9a1f1617ce173..464d6d31ff3e75 100644 --- a/arch/wasm/kernel/irq.c +++ b/arch/wasm/kernel/irq.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,11 +11,47 @@ static DEFINE_PER_CPU(unsigned long, irqflags); static DEFINE_PER_CPU(atomic64_t, irq_pending); +static DEFINE_PER_CPU(u64, timer_deadline_ns); + +void wasm_set_timer_deadline(u64 deadline_ns) +{ + __this_cpu_write(timer_deadline_ns, deadline_ns); +} + +u64 wasm_get_timer_deadline(void) +{ + return __this_cpu_read(timer_deadline_ns); +} void __cpuidle arch_cpu_idle(void) { atomic64_t *pending = this_cpu_ptr(&irq_pending); - __builtin_wasm_memory_atomic_wait64(&pending->counter, 0, -1); + u64 deadline = __this_cpu_read(timer_deadline_ns); + u64 now; + s64 timeout_ns; + int ret; + + if (deadline == 0) { + timeout_ns = -1; // forever + } else { + now = wasm_kernel_get_now_nsec(); + if ((s64)(deadline - now) <= 0) { + __this_cpu_write(timer_deadline_ns, 0); + atomic64_or(1 << TIMER_IRQ, pending); + raw_local_irq_enable(); + return; + } + timeout_ns = deadline - now; + } + + ret = __builtin_wasm_memory_atomic_wait64(&pending->counter, 0, + timeout_ns); + + if (ret == 2 /* timeout reached */) { + __this_cpu_write(timer_deadline_ns, 0); + atomic64_or(1 << TIMER_IRQ, pending); + } + raw_local_irq_enable(); } diff --git a/arch/wasm/kernel/syscall.c b/arch/wasm/kernel/syscall.c index c402bda442b926..ef7d5c25007813 100644 --- a/arch/wasm/kernel/syscall.c +++ b/arch/wasm/kernel/syscall.c @@ -23,6 +23,7 @@ wasm_syscall(long nr, unsigned long arg0, unsigned long arg1, struct pt_regs *regs = current_pt_regs(); long ret; + regs->user_mode = 0; nr = syscall_enter_from_user_mode(regs, nr); if (nr < 0 || nr >= ARRAY_SIZE(syscall_table)) @@ -39,6 +40,7 @@ wasm_syscall(long nr, unsigned long arg0, unsigned long arg1, ret = syscall_table[nr](regs); syscall_exit_to_user_mode(regs); + regs->user_mode = 1; return ret; } diff --git a/arch/wasm/kernel/time.c b/arch/wasm/kernel/time.c index d7543e424e660a..daec0a65f58b67 100644 --- a/arch/wasm/kernel/time.c +++ b/arch/wasm/kernel/time.c @@ -1,12 +1,22 @@ +#include #include +#include #include #include +#include +#include +#include +#include #include #include #include #include extern unsigned long loops_per_jiffy; +extern void wasm_set_timer_deadline(u64 deadline_ns); +extern u64 wasm_get_timer_deadline(void); + +static int timer_irq; void calibrate_delay(void) { loops_per_jiffy = 1000000000 / HZ; @@ -32,9 +42,11 @@ void __const_udelay(unsigned long xloops) __delay(xloops / 0x10c7ul); /* 2**32 / 1000000 (rounded up) */ } -unsigned long long sched_clock(void) { +unsigned long long sched_clock(void) +{ static u64 origin = 0; - if (!origin) origin = wasm_kernel_get_now_nsec(); + if (!origin) + origin = wasm_kernel_get_now_nsec(); return wasm_kernel_get_now_nsec() - origin; } @@ -51,8 +63,72 @@ static struct clocksource clocksource = { .mask = CLOCKSOURCE_MASK(64), }; +static DEFINE_PER_CPU(struct clock_event_device, clockevent); + +static irqreturn_t timer_interrupt(int irq, void *dev) +{ + struct clock_event_device *evt = this_cpu_ptr(&clockevent); + + if (evt->event_handler) + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static int timer_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + u64 now = wasm_kernel_get_now_nsec(); + u64 deadline = now + delta; + wasm_set_timer_deadline(deadline); + return 0; +} + +static int timer_set_oneshot(struct clock_event_device *evt) +{ + return 0; +} + +static int timer_shutdown(struct clock_event_device *evt) +{ + wasm_set_timer_deadline(0); + return 0; +} + +static int timer_starting_cpu(unsigned int cpu) +{ + struct clock_event_device *evt = this_cpu_ptr(&clockevent); + + evt->name = "wasm-timer"; + evt->features = CLOCK_EVT_FEAT_ONESHOT; + evt->rating = 300; + evt->set_next_event = timer_set_next_event; + evt->set_state_oneshot = timer_set_oneshot; + evt->set_state_shutdown = timer_shutdown; + evt->cpumask = cpumask_of(cpu); + evt->irq = timer_irq; + + clockevents_config_and_register(evt, NSEC_PER_SEC, 1000, LONG_MAX); + + return 0; +} + void __init time_init(void) { + int ret; + if (clocksource_register_khz(&clocksource, 1000 * 1000)) panic("unable to register clocksource\n"); + + timer_irq = irq_create_mapping(NULL, TIMER_IRQ); + if (!timer_irq) + panic("unable to create IRQ mapping for timer\n"); + + if (request_irq(timer_irq, timer_interrupt, IRQF_TIMER, "timer", NULL)) + panic("unable to request timer IRQ\n"); + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "wasm/timer:online", + timer_starting_cpu, NULL); + if (ret < 0) + panic("unable to setup CPU hotplug state\n"); } diff --git a/tools/wasm/src/wasm.ts b/tools/wasm/src/wasm.ts index df71321b67fe07..14d8917fe403da 100644 --- a/tools/wasm/src/wasm.ts +++ b/tools/wasm/src/wasm.ts @@ -75,6 +75,8 @@ export interface Imports { }; } +export const HALT_KERNEL = Symbol("halt kernel"); + export function kernel_imports( { is_worker, @@ -111,6 +113,7 @@ export function kernel_imports( halt_worker: () => { if (!is_worker) throw new Error("Halt called in main thread"); self.close(); + throw HALT_KERNEL; }, boot_console_write: (msg, len) => { diff --git a/tools/wasm/src/worker.ts b/tools/wasm/src/worker.ts index c11ca8747d91d1..5e36bd86cfb167 100644 --- a/tools/wasm/src/worker.ts +++ b/tools/wasm/src/worker.ts @@ -1,5 +1,10 @@ import { assert } from "./util.ts"; -import { type Imports, type Instance, kernel_imports } from "./wasm.ts"; +import { + HALT_KERNEL, + type Imports, + type Instance, + kernel_imports, +} from "./wasm.ts"; export interface InitMessage { fn: number; @@ -151,6 +156,7 @@ function user_imports({ call_entry(); } catch (error) { if (error === HALT_USER) continue; + if (error === HALT_KERNEL) throw error; console.log("error running user module:", String(error)); return; } @@ -288,6 +294,11 @@ self.onmessage = (event: MessageEvent) => { }, } satisfies Imports; - const instance = (new WebAssembly.Instance(vmlinux, imports)) as Instance; - instance.exports.__indirect_function_table.get(fn)!(arg); + const instance = new WebAssembly.Instance(vmlinux, imports) as Instance; + try { + instance.exports.__indirect_function_table.get(fn)!(arg); + } catch (error) { + if (error === HALT_KERNEL) return; + throw error; + } };