Skip to content

Commit ad5d112

Browse files
VincentZWCpalmer-dabbelt
authored andcommitted
riscv: use vDSO common flow to reduce the latency of the time-related functions
Even if RISC-V has supported the vDSO feature, the latency of the functions for obtaining the system time is still expensive. It is because these functions still trigger a corresponding system call in the process, which slows down the response time. If we want to remove the system call to reduce the latency, the kernel should have the ability to output the system clock information to userspace. This patch introduces the vDSO common flow to enable the kernel to achieve the above feature and uses "rdtime" instruction to obtain the current time in the user space. Under this condition, the latency cost by the ecall from U-mode to S-mode can be eliminated. After applying this patch, the latency of gettimeofday() measured on the HiFive unleashed board can be reduced by %61. Signed-off-by: Vincent Chen <[email protected]> Reviewed-by: Atish Patra <[email protected]> Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent 05589dd commit ad5d112

File tree

16 files changed

+199
-69
lines changed

16 files changed

+199
-69
lines changed

arch/riscv/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ config 32BIT
1212

1313
config RISCV
1414
def_bool y
15+
select ARCH_CLOCKSOURCE_INIT
1516
select ARCH_HAS_BINFMT_FLAT
1617
select ARCH_HAS_DEBUG_VIRTUAL if MMU
1718
select ARCH_HAS_DEBUG_WX
@@ -31,6 +32,7 @@ config RISCV
3132
select GENERIC_ARCH_TOPOLOGY if SMP
3233
select GENERIC_ATOMIC64 if !64BIT
3334
select GENERIC_CLOCKEVENTS
35+
select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO
3436
select GENERIC_IOREMAP
3537
select GENERIC_IRQ_MULTI_HANDLER
3638
select GENERIC_IRQ_SHOW
@@ -40,6 +42,7 @@ config RISCV
4042
select GENERIC_SMP_IDLE_THREAD
4143
select GENERIC_STRNCPY_FROM_USER if MMU
4244
select GENERIC_STRNLEN_USER if MMU
45+
select GENERIC_TIME_VSYSCALL if MMU && 64BIT
4346
select HANDLE_DOMAIN_IRQ
4447
select HAVE_ARCH_AUDITSYSCALL
4548
select HAVE_ARCH_KASAN if MMU && 64BIT
@@ -53,6 +56,7 @@ config RISCV
5356
select HAVE_DMA_CONTIGUOUS if MMU
5457
select HAVE_EBPF_JIT if MMU
5558
select HAVE_FUTEX_CMPXCHG if FUTEX
59+
select HAVE_GENERIC_VDSO if MMU && 64BIT
5660
select HAVE_PCI
5761
select HAVE_PERF_EVENTS
5862
select HAVE_PERF_REGS

arch/riscv/include/asm/clocksource.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_CLOCKSOURCE_H
3+
#define _ASM_CLOCKSOURCE_H
4+
5+
#include <asm/vdso/clocksource.h>
6+
7+
#endif

arch/riscv/include/asm/processor.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <linux/const.h>
1010

11+
#include <vdso/processor.h>
12+
1113
#include <asm/ptrace.h>
1214

1315
/*
@@ -58,16 +60,6 @@ static inline void release_thread(struct task_struct *dead_task)
5860
extern unsigned long get_wchan(struct task_struct *p);
5961

6062

61-
static inline void cpu_relax(void)
62-
{
63-
#ifdef __riscv_muldiv
64-
int dummy;
65-
/* In lieu of a halt instruction, induce a long-latency stall. */
66-
__asm__ __volatile__ ("div %0, %0, zero" : "=r" (dummy));
67-
#endif
68-
barrier();
69-
}
70-
7163
static inline void wait_for_interrupt(void)
7264
{
7365
__asm__ __volatile__ ("wfi");

arch/riscv/include/asm/vdso.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010

1111
#include <linux/types.h>
1212

13+
#ifndef GENERIC_TIME_VSYSCALL
1314
struct vdso_data {
1415
};
16+
#endif
1517

1618
/*
1719
* The VDSO symbols are mapped into Linux so we can just use regular symbol
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __ASM_VDSOCLOCKSOURCE_H
3+
#define __ASM_VDSOCLOCKSOURCE_H
4+
5+
#define VDSO_ARCH_CLOCKMODES \
6+
VDSO_CLOCKMODE_ARCHTIMER
7+
8+
#endif
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __ASM_VDSO_GETTIMEOFDAY_H
3+
#define __ASM_VDSO_GETTIMEOFDAY_H
4+
5+
#ifndef __ASSEMBLY__
6+
7+
#include <asm/unistd.h>
8+
#include <asm/csr.h>
9+
#include <uapi/linux/time.h>
10+
11+
#define VDSO_HAS_CLOCK_GETRES 1
12+
13+
static __always_inline
14+
int gettimeofday_fallback(struct __kernel_old_timeval *_tv,
15+
struct timezone *_tz)
16+
{
17+
register struct __kernel_old_timeval *tv asm("a0") = _tv;
18+
register struct timezone *tz asm("a1") = _tz;
19+
register long ret asm("a0");
20+
register long nr asm("a7") = __NR_gettimeofday;
21+
22+
asm volatile ("ecall\n"
23+
: "=r" (ret)
24+
: "r"(tv), "r"(tz), "r"(nr)
25+
: "memory");
26+
27+
return ret;
28+
}
29+
30+
static __always_inline
31+
long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
32+
{
33+
register clockid_t clkid asm("a0") = _clkid;
34+
register struct __kernel_timespec *ts asm("a1") = _ts;
35+
register long ret asm("a0");
36+
register long nr asm("a7") = __NR_clock_gettime;
37+
38+
asm volatile ("ecall\n"
39+
: "=r" (ret)
40+
: "r"(clkid), "r"(ts), "r"(nr)
41+
: "memory");
42+
43+
return ret;
44+
}
45+
46+
static __always_inline
47+
int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
48+
{
49+
register clockid_t clkid asm("a0") = _clkid;
50+
register struct __kernel_timespec *ts asm("a1") = _ts;
51+
register long ret asm("a0");
52+
register long nr asm("a7") = __NR_clock_getres;
53+
54+
asm volatile ("ecall\n"
55+
: "=r" (ret)
56+
: "r"(clkid), "r"(ts), "r"(nr)
57+
: "memory");
58+
59+
return ret;
60+
}
61+
62+
static __always_inline u64 __arch_get_hw_counter(s32 clock_mode)
63+
{
64+
/*
65+
* The purpose of csr_read(CSR_TIME) is to trap the system into
66+
* M-mode to obtain the value of CSR_TIME. Hence, unlike other
67+
* architecture, no fence instructions surround the csr_read()
68+
*/
69+
return csr_read(CSR_TIME);
70+
}
71+
72+
static __always_inline const struct vdso_data *__arch_get_vdso_data(void)
73+
{
74+
return _vdso_data;
75+
}
76+
77+
#endif /* !__ASSEMBLY__ */
78+
79+
#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
#ifndef __ASM_VDSO_PROCESSOR_H
3+
#define __ASM_VDSO_PROCESSOR_H
4+
5+
#ifndef __ASSEMBLY__
6+
7+
static inline void cpu_relax(void)
8+
{
9+
#ifdef __riscv_muldiv
10+
int dummy;
11+
/* In lieu of a halt instruction, induce a long-latency stall. */
12+
__asm__ __volatile__ ("div %0, %0, zero" : "=r" (dummy));
13+
#endif
14+
barrier();
15+
}
16+
17+
#endif /* __ASSEMBLY__ */
18+
19+
#endif /* __ASM_VDSO_PROCESSOR_H */
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __ASM_VDSO_VSYSCALL_H
3+
#define __ASM_VDSO_VSYSCALL_H
4+
5+
#ifndef __ASSEMBLY__
6+
7+
#include <linux/timekeeper_internal.h>
8+
#include <vdso/datapage.h>
9+
10+
extern struct vdso_data *vdso_data;
11+
12+
/*
13+
* Update the vDSO data page to keep in sync with kernel timekeeping.
14+
*/
15+
static __always_inline struct vdso_data *__riscv_get_k_vdso_data(void)
16+
{
17+
return vdso_data;
18+
}
19+
20+
#define __arch_get_k_vdso_data __riscv_get_k_vdso_data
21+
22+
/* The asm-generic header needs to be included after the definitions above */
23+
#include <asm-generic/vdso/vsyscall.h>
24+
25+
#endif /* !__ASSEMBLY__ */
26+
27+
#endif /* __ASM_VDSO_VSYSCALL_H */

arch/riscv/kernel/time.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,12 @@ void __init time_init(void)
2626
lpj_fine = riscv_timebase / HZ;
2727
timer_probe();
2828
}
29+
30+
void clocksource_arch_init(struct clocksource *cs)
31+
{
32+
#ifdef CONFIG_GENERIC_GETTIMEOFDAY
33+
cs->vdso_clock_mode = VDSO_CLOCKMODE_ARCHTIMER;
34+
#else
35+
cs->vdso_clock_mode = VDSO_CLOCKMODE_NONE;
36+
#endif
37+
}

arch/riscv/kernel/vdso.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
#include <linux/slab.h>
1212
#include <linux/binfmts.h>
1313
#include <linux/err.h>
14-
14+
#include <asm/page.h>
15+
#ifdef GENERIC_TIME_VSYSCALL
16+
#include <vdso/datapage.h>
17+
#else
1518
#include <asm/vdso.h>
19+
#endif
1620

1721
extern char vdso_start[], vdso_end[];
1822

@@ -26,7 +30,7 @@ static union {
2630
struct vdso_data data;
2731
u8 page[PAGE_SIZE];
2832
} vdso_data_store __page_aligned_data;
29-
static struct vdso_data *vdso_data = &vdso_data_store.data;
33+
struct vdso_data *vdso_data = &vdso_data_store.data;
3034

3135
static int __init vdso_init(void)
3236
{

arch/riscv/kernel/vdso/Makefile

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
# Copied from arch/tile/kernel/vdso/Makefile
33

4+
# Absolute relocation type $(ARCH_REL_TYPE_ABS) needs to be defined before
5+
# the inclusion of generic Makefile.
6+
ARCH_REL_TYPE_ABS := R_RISCV_32|R_RISCV_64|R_RISCV_JUMP_SLOT
7+
include $(srctree)/lib/vdso/Makefile
48
# Symbols present in the vdso
59
vdso-syms = rt_sigreturn
610
ifdef CONFIG_64BIT
7-
vdso-syms += gettimeofday
8-
vdso-syms += clock_gettime
9-
vdso-syms += clock_getres
11+
vdso-syms += vgettimeofday
1012
endif
1113
vdso-syms += getcpu
1214
vdso-syms += flush_icache
1315

1416
# Files to link into the vdso
1517
obj-vdso = $(patsubst %, %.o, $(vdso-syms)) note.o
1618

19+
ifneq ($(c-gettimeofday-y),)
20+
CFLAGS_vgettimeofday.o += -include $(c-gettimeofday-y)
21+
endif
22+
1723
# Build rules
1824
targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.lds vdso-dummy.o
1925
obj-vdso := $(addprefix $(obj)/, $(obj-vdso))

arch/riscv/kernel/vdso/clock_getres.S

Lines changed: 0 additions & 18 deletions
This file was deleted.

arch/riscv/kernel/vdso/clock_gettime.S

Lines changed: 0 additions & 18 deletions
This file was deleted.

arch/riscv/kernel/vdso/gettimeofday.S

Lines changed: 0 additions & 18 deletions
This file was deleted.

arch/riscv/kernel/vdso/vdso.lds.S

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
/*
33
* Copyright (C) 2012 Regents of the University of California
44
*/
5+
#include <asm/page.h>
56

67
OUTPUT_ARCH(riscv)
78

89
SECTIONS
910
{
11+
PROVIDE(_vdso_data = . + PAGE_SIZE);
1012
. = SIZEOF_HEADERS;
1113

1214
.hash : { *(.hash) } :text
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copied from arch/arm64/kernel/vdso/vgettimeofday.c
4+
*
5+
* Copyright (C) 2018 ARM Ltd.
6+
* Copyright (C) 2020 SiFive
7+
*/
8+
9+
#include <linux/time.h>
10+
#include <linux/types.h>
11+
12+
int __vdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts)
13+
{
14+
return __cvdso_clock_gettime(clock, ts);
15+
}
16+
17+
int __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz)
18+
{
19+
return __cvdso_gettimeofday(tv, tz);
20+
}
21+
22+
int __vdso_clock_getres(clockid_t clock_id, struct __kernel_timespec *res)
23+
{
24+
return __cvdso_clock_getres(clock_id, res);
25+
}

0 commit comments

Comments
 (0)