Download raw body.
riscv64: use sstc for clocks when available
On Mon, Jul 13, 2026 at 09:18:20AM -0700, Nick Owens wrote:
> hi,
Hi, sorry for the delay ;)
> 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/
Getting rid of this overhead appears desirable, working around a bug
in a hypervisor is just a bonus...
No visible regression on my k1 which exposes Sstc. Please find a
tweaked diff below: I'd rather have the check for the feature in a
single place. The "timer_set_timer" name is admittedly a bit
redundant, suggestions welcome.
I'll commit this in a few days unless I hear objections. Tests & oks
welcome.
Index: clock.c
===================================================================
RCS file: /cvs/src/sys/arch/riscv64/riscv64/clock.c,v
diff -u -p -r1.14 clock.c
--- clock.c 27 Jan 2024 12:05:40 -0000 1.14
+++ clock.c 24 Jul 2026 14:54:28 -0000
@@ -25,6 +25,7 @@
#include <sys/stdint.h>
#include <sys/timetc.h>
+#include <machine/elf.h>
#include <machine/cpufunc.h>
#include <machine/sbi.h>
@@ -60,6 +61,15 @@ static struct timecounter tb_timecounter
void (*cpu_startclock_fcn)(void) = timer_startclock;
int clock_intr(void *);
+static inline void
+timer_set_timer(uint64_t nsecs)
+{
+ if (riscv_hwcap & HWCAP_ISA_SSTC)
+ csr_write(stimecmp, nsecs);
+ else
+ sbi_set_timer(nsecs);
+}
+
void
timer_rearm(void *unused, uint64_t nsecs)
{
@@ -68,13 +78,13 @@ 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);
+ timer_set_timer(rdtime() + cycles);
}
void
timer_trigger(void *unused)
{
- sbi_set_timer(0);
+ timer_set_timer(0);
}
u_int
@@ -125,7 +135,7 @@ clock_intr(void *frame)
struct cpu_info *ci = curcpu();
int s;
- sbi_set_timer(UINT64_MAX); /* clear timer interrupt */
+ timer_set_timer(UINT64_MAX); /* clear timer interrupt */
/*
* If the clock interrupt is masked, defer all clock interrupt
--
jca
riscv64: use sstc for clocks when available