Download raw body.
riscv64: use sstc for clocks when available
hi,
this implements support for sstc extension. with sstc, we can write the
stimecmp csr directly instead of needing an sbi ecall to set the timer
comparison register.
both the k1 and k3 support it, and since it's a direct register write it
takes only ~10ns instead of several hundred ns for the ecall and
possible hypervisor emulation in the case of linux kvm.
i've tested on orangepi rv2 (k1) directly, and on the k3 under linux
kvm. fallback path tested on kvm by disabling sstc in the advertised
features of qemu kvm as well. without sstc, the k1 takes about 250ns and
the k3 + kvm takes about 600ns. with this change, both are around 10ns
per timer write.
this also conveniently works around a bug in upstream kvm where timer
writes via sbi ecall can get lost if there's a context switch at the
wrong time, which makes kvm guest timers permanently stop firing. see
https://lore.kernel.org/all/20260609183921.42043-1-jsun@junsun.net/
diff --git a/sys/arch/riscv64/riscv64/clock.c b/sys/arch/riscv64/riscv64/clock.c
index f9775297737..66e300560bc 100644
--- a/sys/arch/riscv64/riscv64/clock.c
+++ b/sys/arch/riscv64/riscv64/clock.c
@@ -25,6 +25,7 @@
#include <sys/stdint.h>
#include <sys/timetc.h>
+#include <machine/elf.h>
#include <machine/cpufunc.h>
#include <machine/sbi.h>
@@ -68,13 +69,20 @@ timer_rearm(void *unused, uint64_t nsecs)
if (nsecs > timer_nsec_max)
nsecs = timer_nsec_max;
cycles = (nsecs * timer_nsec_cycle_ratio) >> 32;
- sbi_set_timer(rdtime() + cycles);
+
+ if (riscv_hwcap & HWCAP_ISA_SSTC)
+ csr_write(stimecmp, rdtime() + cycles);
+ else
+ sbi_set_timer(rdtime() + cycles);
}
void
timer_trigger(void *unused)
{
- sbi_set_timer(0);
+ if (riscv_hwcap & HWCAP_ISA_SSTC)
+ csr_write(stimecmp, 0);
+ else
+ sbi_set_timer(0);
}
u_int
@@ -125,7 +133,11 @@ clock_intr(void *frame)
struct cpu_info *ci = curcpu();
int s;
- sbi_set_timer(UINT64_MAX); /* clear timer interrupt */
+ /* clear timer interrupt */
+ if (riscv_hwcap & HWCAP_ISA_SSTC)
+ csr_write(stimecmp, UINT64_MAX);
+ else
+ sbi_set_timer(UINT64_MAX);
/*
* If the clock interrupt is masked, defer all clock interrupt
riscv64: use sstc for clocks when available