Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion arch/riscv/include/asm/hwprobe.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,11 @@ static inline bool riscv_hwprobe_pair_cmp(struct riscv_hwprobe *pair,

return pair->value == other_pair->value;
}

#ifdef CONFIG_MMU
void riscv_hwprobe_register_async_probe(void);
void riscv_hwprobe_complete_async_probe(void);
#else
static inline void riscv_hwprobe_register_async_probe(void) {}
static inline void riscv_hwprobe_complete_async_probe(void) {}
#endif
#endif
6 changes: 6 additions & 0 deletions arch/riscv/include/asm/vdso/arch_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ struct vdso_arch_data {

/* Boolean indicating all CPUs have the same static hwprobe values. */
__u8 homogeneous_cpus;

/*
* A gate to check and see if the hwprobe data is actually ready, as
* probing is deferred to avoid boot slowdowns.
*/
__u8 ready;
};

#endif /* __RISCV_ASM_VDSO_ARCH_DATA_H */
71 changes: 58 additions & 13 deletions arch/riscv/kernel/sys_hwprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* more details.
*/
#include <linux/syscalls.h>
#include <linux/completion.h>
#include <linux/atomic.h>
#include <asm/cacheflush.h>
#include <asm/cpufeature.h>
#include <asm/hwprobe.h>
Expand Down Expand Up @@ -452,28 +454,36 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
return 0;
}

static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
size_t pair_count, size_t cpusetsize,
unsigned long __user *cpus_user,
unsigned int flags)
{
if (flags & RISCV_HWPROBE_WHICH_CPUS)
return hwprobe_get_cpus(pairs, pair_count, cpusetsize,
cpus_user, flags);
#ifdef CONFIG_MMU

return hwprobe_get_values(pairs, pair_count, cpusetsize,
cpus_user, flags);
static DECLARE_COMPLETION(boot_probes_done);
static atomic_t pending_boot_probes = ATOMIC_INIT(1);

void riscv_hwprobe_register_async_probe(void)
{
atomic_inc(&pending_boot_probes);
}

#ifdef CONFIG_MMU
void riscv_hwprobe_complete_async_probe(void)
{
if (atomic_dec_and_test(&pending_boot_probes))
complete(&boot_probes_done);
}

static int __init init_hwprobe_vdso_data(void)
static int complete_hwprobe_vdso_data(void)
{
struct vdso_arch_data *avd = vdso_k_arch_data;
u64 id_bitsmash = 0;
struct riscv_hwprobe pair;
int key;

/* We've probably already produced these values. */
if (likely(avd->ready))
return 0;

if (unlikely(!atomic_dec_and_test(&pending_boot_probes)))
wait_for_completion(&boot_probes_done);

/*
* Initialize vDSO data with the answers for the "all CPUs" case, to
* save a syscall in the common case.
Expand Down Expand Up @@ -501,13 +511,48 @@ static int __init init_hwprobe_vdso_data(void)
* vDSO should defer to the kernel for exotic cpu masks.
*/
avd->homogeneous_cpus = id_bitsmash != 0 && id_bitsmash != -1;

/*
* Make sure all the VDSO values are visible before we look at them.
* This pairs with the implicit "no speculativly visible accesses"
* barrier in the VDSO hwprobe code.
*/
smp_wmb();
avd->ready = true;
return 0;
}

static int __init init_hwprobe_vdso_data(void)
{
struct vdso_arch_data *avd = vdso_k_arch_data;

/*
* Prevent the vDSO cached values from being used, as they're not ready
* yet.
*/
avd->ready = false;
return 0;
}

arch_initcall_sync(init_hwprobe_vdso_data);
late_initcall(init_hwprobe_vdso_data);

#endif /* CONFIG_MMU */

static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
size_t pair_count, size_t cpusetsize,
unsigned long __user *cpus_user,
unsigned int flags)
{
complete_hwprobe_vdso_data();

if (flags & RISCV_HWPROBE_WHICH_CPUS)
return hwprobe_get_cpus(pairs, pair_count, cpusetsize,
cpus_user, flags);

return hwprobe_get_values(pairs, pair_count, cpusetsize,
cpus_user, flags);
}

SYSCALL_DEFINE5(riscv_hwprobe, struct riscv_hwprobe __user *, pairs,
size_t, pair_count, size_t, cpusetsize, unsigned long __user *,
cpus, unsigned int, flags)
Expand Down
9 changes: 7 additions & 2 deletions arch/riscv/kernel/unaligned_access_speed.c
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus
static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
{
schedule_on_each_cpu(check_vector_unaligned_access);
riscv_hwprobe_complete_async_probe();

return 0;
}
Expand Down Expand Up @@ -473,8 +474,12 @@ static int __init check_unaligned_access_all_cpus(void)
per_cpu(vector_misaligned_access, cpu) = unaligned_vector_speed_param;
} else if (!check_vector_unaligned_access_emulated_all_cpus() &&
IS_ENABLED(CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS)) {
kthread_run(vec_check_unaligned_access_speed_all_cpus,
NULL, "vec_check_unaligned_access_speed_all_cpus");
riscv_hwprobe_register_async_probe();
if (IS_ERR(kthread_run(vec_check_unaligned_access_speed_all_cpus,
NULL, "vec_check_unaligned_access_speed_all_cpus"))) {
pr_warn("Failed to create vec_unalign_check kthread\n");
riscv_hwprobe_complete_async_probe();
}
}

/*
Expand Down
2 changes: 1 addition & 1 deletion arch/riscv/kernel/vdso/hwprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
* homogeneous, then this function can handle requests for arbitrary
* masks.
*/
if ((flags != 0) || (!all_cpus && !avd->homogeneous_cpus))
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. */
Expand Down
Loading