mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 01:49:46 +00:00
The hwprobe vDSO data for some keys, like MISALIGNED_VECTOR_PERF,
is determined by an asynchronous kthread. This can create a race
condition where the kthread finishes after the vDSO data has
already been populated, causing userspace to read stale values.
To fix this race, a new 'ready' flag is added to the vDSO data,
initialized to 'false' during arch_initcall_sync. This flag is
checked by both the vDSO's user-space code and the riscv_hwprobe
syscall. The syscall serves as a one-time gate, using a completion
to wait for any pending probes before populating the data and
setting the flag to 'true', thus ensuring userspace reads fresh
values on its first request.
Reported-by: Tsukasa OI <research_trasio@irq.a4lg.com>
Closes: https://lore.kernel.org/linux-riscv/760d637b-b13b-4518-b6bf-883d55d44e7f@irq.a4lg.com/
Fixes: e7c9d66e31 ("RISC-V: Report vector unaligned access speed hwprobe")
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Alexandre Ghiti <alexghiti@rivosinc.com>
Cc: Olof Johansson <olof@lixom.net>
Cc: stable@vger.kernel.org
Reviewed-by: Alexandre Ghiti <alexghiti@rivosinc.com>
Co-developed-by: Palmer Dabbelt <palmer@dabbelt.com>
Signed-off-by: Palmer Dabbelt <palmer@dabbelt.com>
Signed-off-by: Jingwei Wang <wangjingwei@iscas.ac.cn>
Link: https://lore.kernel.org/r/20250811142035.105820-1-wangjingwei@iscas.ac.cn
[pjw@kernel.org: fix checkpatch issues]
Signed-off-by: Paul Walmsley <pjw@kernel.org>
115 lines
2.8 KiB
C
115 lines
2.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2023 Rivos, Inc
|
|
*/
|
|
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
#include <vdso/datapage.h>
|
|
#include <vdso/helpers.h>
|
|
|
|
extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
|
|
size_t cpusetsize, unsigned long *cpus,
|
|
unsigned int flags);
|
|
|
|
static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
|
|
size_t cpusetsize, unsigned long *cpus,
|
|
unsigned int flags)
|
|
{
|
|
const struct vdso_arch_data *avd = &vdso_u_arch_data;
|
|
bool all_cpus = !cpusetsize && !cpus;
|
|
struct riscv_hwprobe *p = pairs;
|
|
struct riscv_hwprobe *end = pairs + pair_count;
|
|
|
|
/*
|
|
* Defer to the syscall for exotic requests. The vdso has answers
|
|
* stashed away only for the "all cpus" case. If all CPUs are
|
|
* homogeneous, then this function can handle requests for arbitrary
|
|
* masks.
|
|
*/
|
|
if (flags != 0 || (!all_cpus && !avd->homogeneous_cpus) || unlikely(!avd->ready))
|
|
return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
|
|
|
|
/* This is something we can handle, fill out the pairs. */
|
|
while (p < end) {
|
|
if (riscv_hwprobe_key_is_valid(p->key)) {
|
|
p->value = avd->all_cpu_hwprobe_values[p->key];
|
|
|
|
} else {
|
|
p->key = -1;
|
|
p->value = 0;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count,
|
|
size_t cpusetsize, unsigned long *cpus,
|
|
unsigned int flags)
|
|
{
|
|
const struct vdso_arch_data *avd = &vdso_u_arch_data;
|
|
struct riscv_hwprobe *p = pairs;
|
|
struct riscv_hwprobe *end = pairs + pair_count;
|
|
unsigned char *c = (unsigned char *)cpus;
|
|
bool empty_cpus = true;
|
|
bool clear_all = false;
|
|
int i;
|
|
|
|
if (!cpusetsize || !cpus)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < cpusetsize; i++) {
|
|
if (c[i]) {
|
|
empty_cpus = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS || !avd->homogeneous_cpus)
|
|
return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
|
|
|
|
while (p < end) {
|
|
if (riscv_hwprobe_key_is_valid(p->key)) {
|
|
struct riscv_hwprobe t = {
|
|
.key = p->key,
|
|
.value = avd->all_cpu_hwprobe_values[p->key],
|
|
};
|
|
|
|
if (!riscv_hwprobe_pair_cmp(&t, p))
|
|
clear_all = true;
|
|
} else {
|
|
clear_all = true;
|
|
p->key = -1;
|
|
p->value = 0;
|
|
}
|
|
p++;
|
|
}
|
|
|
|
if (clear_all) {
|
|
for (i = 0; i < cpusetsize; i++)
|
|
c[i] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Add a prototype to avoid -Wmissing-prototypes warning. */
|
|
int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
|
|
size_t cpusetsize, unsigned long *cpus,
|
|
unsigned int flags);
|
|
|
|
int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
|
|
size_t cpusetsize, unsigned long *cpus,
|
|
unsigned int flags)
|
|
{
|
|
if (flags & RISCV_HWPROBE_WHICH_CPUS)
|
|
return riscv_vdso_get_cpus(pairs, pair_count, cpusetsize,
|
|
cpus, flags);
|
|
|
|
return riscv_vdso_get_values(pairs, pair_count, cpusetsize,
|
|
cpus, flags);
|
|
}
|