Skip to content

Commit aa5af0a

Browse files
evangreenpalmer-dabbelt
authored andcommitted
RISC-V: Add hwprobe vDSO function and data
Add a vDSO function __vdso_riscv_hwprobe, which can sit in front of the riscv_hwprobe syscall and answer common queries. We stash a copy of static answers for the "all CPUs" case in the vDSO data page. This data is private to the vDSO, so we can decide later to change what's stored there or under what conditions we defer to the syscall. Currently all data can be discovered at boot, so the vDSO function answers all queries when the cpumask is set to the "all CPUs" hint. There's also a boolean in the data that lets the vDSO function know that all CPUs are the same. In that case, the vDSO will also answer queries for arbitrary CPU masks in addition to the "all CPUs" hint. Signed-off-by: Evan Green <evan@rivosinc.com> Link: https://lore.kernel.org/r/20230407231103.2622178-7-evan@rivosinc.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1 parent 287dcc2 commit aa5af0a

10 files changed

Lines changed: 146 additions & 7 deletions

File tree

arch/riscv/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ config RISCV
3333
select ARCH_HAS_STRICT_MODULE_RWX if MMU && !XIP_KERNEL
3434
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
3535
select ARCH_HAS_UBSAN_SANITIZE_ALL
36+
select ARCH_HAS_VDSO_DATA
3637
select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX
3738
select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
3839
select ARCH_STACKWALK

arch/riscv/include/asm/vdso/data.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __RISCV_ASM_VDSO_DATA_H
3+
#define __RISCV_ASM_VDSO_DATA_H
4+
5+
#include <linux/types.h>
6+
#include <vdso/datapage.h>
7+
#include <asm/hwprobe.h>
8+
9+
struct arch_vdso_data {
10+
/* Stash static answers to the hwprobe queries when all CPUs are selected. */
11+
__u64 all_cpu_hwprobe_values[RISCV_HWPROBE_MAX_KEY + 1];
12+
13+
/* Boolean indicating all CPUs have the same static hwprobe values. */
14+
__u8 homogeneous_cpus;
15+
};
16+
17+
#endif /* __RISCV_ASM_VDSO_DATA_H */

arch/riscv/include/asm/vdso/gettimeofday.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
#include <asm/csr.h>
1010
#include <uapi/linux/time.h>
1111

12+
/*
13+
* 32-bit land is lacking generic time vsyscalls as well as the legacy 32-bit
14+
* time syscalls like gettimeofday. Skip these definitions since on 32-bit.
15+
*/
16+
#ifdef CONFIG_GENERIC_TIME_VSYSCALL
17+
1218
#define VDSO_HAS_CLOCK_GETRES 1
1319

1420
static __always_inline
@@ -60,6 +66,8 @@ int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
6066
return ret;
6167
}
6268

69+
#endif /* CONFIG_GENERIC_TIME_VSYSCALL */
70+
6371
static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
6472
const struct vdso_data *vd)
6573
{

arch/riscv/kernel/compat_vdso/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ targets := $(obj-compat_vdso) compat_vdso.so compat_vdso.so.dbg compat_vdso.lds
2222
obj-compat_vdso := $(addprefix $(obj)/, $(obj-compat_vdso))
2323

2424
obj-y += compat_vdso.o
25-
CPPFLAGS_compat_vdso.lds += -P -C -U$(ARCH)
25+
CPPFLAGS_compat_vdso.lds += -P -C -DCOMPAT_VDSO -U$(ARCH)
2626

2727
# Disable profiling and instrumentation for VDSO code
2828
GCOV_PROFILE := n

arch/riscv/kernel/sys_riscv.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <asm/uaccess.h>
1515
#include <asm/unistd.h>
1616
#include <asm-generic/mman-common.h>
17+
#include <vdso/vsyscall.h>
1718

1819
static long riscv_sys_mmap(unsigned long addr, unsigned long len,
1920
unsigned long prot, unsigned long flags,
@@ -243,6 +244,50 @@ static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
243244
return 0;
244245
}
245246

247+
#ifdef CONFIG_MMU
248+
249+
static int __init init_hwprobe_vdso_data(void)
250+
{
251+
struct vdso_data *vd = __arch_get_k_vdso_data();
252+
struct arch_vdso_data *avd = &vd->arch_data;
253+
u64 id_bitsmash = 0;
254+
struct riscv_hwprobe pair;
255+
int key;
256+
257+
/*
258+
* Initialize vDSO data with the answers for the "all CPUs" case, to
259+
* save a syscall in the common case.
260+
*/
261+
for (key = 0; key <= RISCV_HWPROBE_MAX_KEY; key++) {
262+
pair.key = key;
263+
hwprobe_one_pair(&pair, cpu_online_mask);
264+
265+
WARN_ON_ONCE(pair.key < 0);
266+
267+
avd->all_cpu_hwprobe_values[key] = pair.value;
268+
/*
269+
* Smash together the vendor, arch, and impl IDs to see if
270+
* they're all 0 or any negative.
271+
*/
272+
if (key <= RISCV_HWPROBE_KEY_MIMPID)
273+
id_bitsmash |= pair.value;
274+
}
275+
276+
/*
277+
* If the arch, vendor, and implementation ID are all the same across
278+
* all harts, then assume all CPUs are the same, and allow the vDSO to
279+
* answer queries for arbitrary masks. However if all values are 0 (not
280+
* populated) or any value returns -1 (varies across CPUs), then the
281+
* vDSO should defer to the kernel for exotic cpu masks.
282+
*/
283+
avd->homogeneous_cpus = (id_bitsmash > 0);
284+
return 0;
285+
}
286+
287+
arch_initcall_sync(init_hwprobe_vdso_data);
288+
289+
#endif /* CONFIG_MMU */
290+
246291
SYSCALL_DEFINE5(riscv_hwprobe, struct riscv_hwprobe __user *, pairs,
247292
size_t, pair_count, size_t, cpu_count, unsigned long __user *,
248293
cpus, unsigned int, flags)

arch/riscv/kernel/vdso.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,7 @@
1414
#include <asm/page.h>
1515
#include <asm/vdso.h>
1616
#include <linux/time_namespace.h>
17-
18-
#ifdef CONFIG_GENERIC_TIME_VSYSCALL
1917
#include <vdso/datapage.h>
20-
#else
21-
struct vdso_data {
22-
};
23-
#endif
2418

2519
enum vvar_pages {
2620
VVAR_DATA_PAGE_OFFSET,

arch/riscv/kernel/vdso/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ vdso-syms += vgettimeofday
1212
endif
1313
vdso-syms += getcpu
1414
vdso-syms += flush_icache
15+
vdso-syms += hwprobe
16+
vdso-syms += sys_hwprobe
1517

1618
# Files to link into the vdso
1719
obj-vdso = $(patsubst %, %.o, $(vdso-syms)) note.o
@@ -23,6 +25,8 @@ ifneq ($(c-gettimeofday-y),)
2325
CFLAGS_vgettimeofday.o += -fPIC -include $(c-gettimeofday-y)
2426
endif
2527

28+
CFLAGS_hwprobe.o += -fPIC
29+
2630
# Build rules
2731
targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.lds
2832
obj-vdso := $(addprefix $(obj)/, $(obj-vdso))

arch/riscv/kernel/vdso/hwprobe.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright 2023 Rivos, Inc
4+
*/
5+
6+
#include <linux/types.h>
7+
#include <vdso/datapage.h>
8+
#include <vdso/helpers.h>
9+
10+
extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
11+
size_t cpu_count, unsigned long *cpus,
12+
unsigned int flags);
13+
14+
/* Add a prototype to avoid -Wmissing-prototypes warning. */
15+
int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
16+
size_t cpu_count, unsigned long *cpus,
17+
unsigned int flags);
18+
19+
int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
20+
size_t cpu_count, unsigned long *cpus,
21+
unsigned int flags)
22+
{
23+
const struct vdso_data *vd = __arch_get_vdso_data();
24+
const struct arch_vdso_data *avd = &vd->arch_data;
25+
bool all_cpus = !cpu_count && !cpus;
26+
struct riscv_hwprobe *p = pairs;
27+
struct riscv_hwprobe *end = pairs + pair_count;
28+
29+
/*
30+
* Defer to the syscall for exotic requests. The vdso has answers
31+
* stashed away only for the "all cpus" case. If all CPUs are
32+
* homogeneous, then this function can handle requests for arbitrary
33+
* masks.
34+
*/
35+
if ((flags != 0) || (!all_cpus && !avd->homogeneous_cpus))
36+
return riscv_hwprobe(pairs, pair_count, cpu_count, cpus, flags);
37+
38+
/* This is something we can handle, fill out the pairs. */
39+
while (p < end) {
40+
if (p->key <= RISCV_HWPROBE_MAX_KEY) {
41+
p->value = avd->all_cpu_hwprobe_values[p->key];
42+
43+
} else {
44+
p->key = -1;
45+
p->value = 0;
46+
}
47+
48+
p++;
49+
}
50+
51+
return 0;
52+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/* Copyright (C) 2023 Rivos, Inc */
3+
4+
#include <linux/linkage.h>
5+
#include <asm/unistd.h>
6+
7+
.text
8+
ENTRY(riscv_hwprobe)
9+
.cfi_startproc
10+
li a7, __NR_riscv_hwprobe
11+
ecall
12+
ret
13+
14+
.cfi_endproc
15+
ENDPROC(riscv_hwprobe)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ VERSION
8282
#endif
8383
__vdso_getcpu;
8484
__vdso_flush_icache;
85+
#ifndef COMPAT_VDSO
86+
__vdso_riscv_hwprobe;
87+
#endif
8588
local: *;
8689
};
8790
}

0 commit comments

Comments
 (0)