Skip to content

Commit e178bf1

Browse files
Andrew Jonespalmer-dabbelt
authored andcommitted
RISC-V: hwprobe: Introduce which-cpus flag
Introduce the first flag for the hwprobe syscall. The flag basically reverses its behavior, i.e. instead of populating the values of keys for a given set of cpus, the set of cpus after the call is the result of finding a set which supports the values of the keys. In order to do this, we implement a pair compare function which takes the type of value (a single value vs. a bitmask of booleans) into consideration. We also implement vdso support for the new flag. Signed-off-by: Andrew Jones <ajones@ventanamicro.com> Reviewed-by: Evan Green <evan@rivosinc.com> Link: https://lore.kernel.org/r/20231122164700.127954-9-ajones@ventanamicro.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1 parent 53b2b22 commit e178bf1

5 files changed

Lines changed: 204 additions & 14 deletions

File tree

Documentation/arch/riscv/hwprobe.rst

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,21 @@ arch, impl), the returned value will only be valid if all CPUs in the given set
2525
have the same value. Otherwise -1 will be returned. For boolean-like keys, the
2626
value returned will be a logical AND of the values for the specified CPUs.
2727
Usermode can supply NULL for ``cpus`` and 0 for ``cpusetsize`` as a shortcut for
28-
all online CPUs. There are currently no flags, this value must be zero for
29-
future compatibility.
28+
all online CPUs. The currently supported flags are:
29+
30+
* :c:macro:`RISCV_HWPROBE_WHICH_CPUS`: This flag basically reverses the behavior
31+
of sys_riscv_hwprobe(). Instead of populating the values of keys for a given
32+
set of CPUs, the values of each key are given and the set of CPUs is reduced
33+
by sys_riscv_hwprobe() to only those which match each of the key-value pairs.
34+
How matching is done depends on the key type. For value-like keys, matching
35+
means to be the exact same as the value. For boolean-like keys, matching
36+
means the result of a logical AND of the pair's value with the CPU's value is
37+
exactly the same as the pair's value. Additionally, when ``cpus`` is an empty
38+
set, then it is initialized to all online CPUs which fit within it, i.e. the
39+
CPU set returned is the reduction of all the online CPUs which can be
40+
represented with a CPU set of size ``cpusetsize``.
41+
42+
All other flags are reserved for future compatibility and must be zero.
3043

3144
On success 0 is returned, on failure a negative error code is returned.
3245

arch/riscv/include/asm/hwprobe.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,28 @@ static inline bool riscv_hwprobe_key_is_valid(__s64 key)
1515
return key >= 0 && key <= RISCV_HWPROBE_MAX_KEY;
1616
}
1717

18+
static inline bool hwprobe_key_is_bitmask(__s64 key)
19+
{
20+
switch (key) {
21+
case RISCV_HWPROBE_KEY_BASE_BEHAVIOR:
22+
case RISCV_HWPROBE_KEY_IMA_EXT_0:
23+
case RISCV_HWPROBE_KEY_CPUPERF_0:
24+
return true;
25+
}
26+
27+
return false;
28+
}
29+
30+
static inline bool riscv_hwprobe_pair_cmp(struct riscv_hwprobe *pair,
31+
struct riscv_hwprobe *other_pair)
32+
{
33+
if (pair->key != other_pair->key)
34+
return false;
35+
36+
if (hwprobe_key_is_bitmask(pair->key))
37+
return (pair->value & other_pair->value) == other_pair->value;
38+
39+
return pair->value == other_pair->value;
40+
}
41+
1842
#endif

arch/riscv/include/uapi/asm/hwprobe.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,7 @@ struct riscv_hwprobe {
4040
#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6
4141
/* Increase RISCV_HWPROBE_MAX_KEY when adding items. */
4242

43+
/* Flags */
44+
#define RISCV_HWPROBE_WHICH_CPUS (1 << 0)
45+
4346
#endif

arch/riscv/kernel/sys_hwprobe.c

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,10 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair,
179179
}
180180
}
181181

182-
static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
183-
size_t pair_count, size_t cpusetsize,
184-
unsigned long __user *cpus_user,
185-
unsigned int flags)
182+
static int hwprobe_get_values(struct riscv_hwprobe __user *pairs,
183+
size_t pair_count, size_t cpusetsize,
184+
unsigned long __user *cpus_user,
185+
unsigned int flags)
186186
{
187187
size_t out;
188188
int ret;
@@ -236,6 +236,92 @@ static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
236236
return 0;
237237
}
238238

239+
static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
240+
size_t pair_count, size_t cpusetsize,
241+
unsigned long __user *cpus_user,
242+
unsigned int flags)
243+
{
244+
cpumask_t cpus, one_cpu;
245+
bool clear_all = false;
246+
size_t i;
247+
int ret;
248+
249+
if (flags != RISCV_HWPROBE_WHICH_CPUS)
250+
return -EINVAL;
251+
252+
if (!cpusetsize || !cpus_user)
253+
return -EINVAL;
254+
255+
if (cpusetsize > cpumask_size())
256+
cpusetsize = cpumask_size();
257+
258+
ret = copy_from_user(&cpus, cpus_user, cpusetsize);
259+
if (ret)
260+
return -EFAULT;
261+
262+
if (cpumask_empty(&cpus))
263+
cpumask_copy(&cpus, cpu_online_mask);
264+
265+
cpumask_and(&cpus, &cpus, cpu_online_mask);
266+
267+
cpumask_clear(&one_cpu);
268+
269+
for (i = 0; i < pair_count; i++) {
270+
struct riscv_hwprobe pair, tmp;
271+
int cpu;
272+
273+
ret = copy_from_user(&pair, &pairs[i], sizeof(pair));
274+
if (ret)
275+
return -EFAULT;
276+
277+
if (!riscv_hwprobe_key_is_valid(pair.key)) {
278+
clear_all = true;
279+
pair = (struct riscv_hwprobe){ .key = -1, };
280+
ret = copy_to_user(&pairs[i], &pair, sizeof(pair));
281+
if (ret)
282+
return -EFAULT;
283+
}
284+
285+
if (clear_all)
286+
continue;
287+
288+
tmp = (struct riscv_hwprobe){ .key = pair.key, };
289+
290+
for_each_cpu(cpu, &cpus) {
291+
cpumask_set_cpu(cpu, &one_cpu);
292+
293+
hwprobe_one_pair(&tmp, &one_cpu);
294+
295+
if (!riscv_hwprobe_pair_cmp(&tmp, &pair))
296+
cpumask_clear_cpu(cpu, &cpus);
297+
298+
cpumask_clear_cpu(cpu, &one_cpu);
299+
}
300+
}
301+
302+
if (clear_all)
303+
cpumask_clear(&cpus);
304+
305+
ret = copy_to_user(cpus_user, &cpus, cpusetsize);
306+
if (ret)
307+
return -EFAULT;
308+
309+
return 0;
310+
}
311+
312+
static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
313+
size_t pair_count, size_t cpusetsize,
314+
unsigned long __user *cpus_user,
315+
unsigned int flags)
316+
{
317+
if (flags & RISCV_HWPROBE_WHICH_CPUS)
318+
return hwprobe_get_cpus(pairs, pair_count, cpusetsize,
319+
cpus_user, flags);
320+
321+
return hwprobe_get_values(pairs, pair_count, cpusetsize,
322+
cpus_user, flags);
323+
}
324+
239325
#ifdef CONFIG_MMU
240326

241327
static int __init init_hwprobe_vdso_data(void)

arch/riscv/kernel/vdso/hwprobe.c

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright 2023 Rivos, Inc
44
*/
55

6+
#include <linux/string.h>
67
#include <linux/types.h>
78
#include <vdso/datapage.h>
89
#include <vdso/helpers.h>
@@ -11,14 +12,9 @@ extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
1112
size_t cpusetsize, unsigned long *cpus,
1213
unsigned int flags);
1314

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 cpusetsize, unsigned long *cpus,
17-
unsigned int flags);
18-
19-
int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
20-
size_t cpusetsize, unsigned long *cpus,
21-
unsigned int flags)
15+
static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
16+
size_t cpusetsize, unsigned long *cpus,
17+
unsigned int flags)
2218
{
2319
const struct vdso_data *vd = __arch_get_vdso_data();
2420
const struct arch_vdso_data *avd = &vd->arch_data;
@@ -50,3 +46,71 @@ int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
5046

5147
return 0;
5248
}
49+
50+
static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count,
51+
size_t cpusetsize, unsigned long *cpus,
52+
unsigned int flags)
53+
{
54+
const struct vdso_data *vd = __arch_get_vdso_data();
55+
const struct arch_vdso_data *avd = &vd->arch_data;
56+
struct riscv_hwprobe *p = pairs;
57+
struct riscv_hwprobe *end = pairs + pair_count;
58+
unsigned char *c = (unsigned char *)cpus;
59+
bool empty_cpus = true;
60+
bool clear_all = false;
61+
int i;
62+
63+
if (!cpusetsize || !cpus)
64+
return -EINVAL;
65+
66+
for (i = 0; i < cpusetsize; i++) {
67+
if (c[i]) {
68+
empty_cpus = false;
69+
break;
70+
}
71+
}
72+
73+
if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS || !avd->homogeneous_cpus)
74+
return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
75+
76+
while (p < end) {
77+
if (riscv_hwprobe_key_is_valid(p->key)) {
78+
struct riscv_hwprobe t = {
79+
.key = p->key,
80+
.value = avd->all_cpu_hwprobe_values[p->key],
81+
};
82+
83+
if (!riscv_hwprobe_pair_cmp(&t, p))
84+
clear_all = true;
85+
} else {
86+
clear_all = true;
87+
p->key = -1;
88+
p->value = 0;
89+
}
90+
p++;
91+
}
92+
93+
if (clear_all) {
94+
for (i = 0; i < cpusetsize; i++)
95+
c[i] = 0;
96+
}
97+
98+
return 0;
99+
}
100+
101+
/* Add a prototype to avoid -Wmissing-prototypes warning. */
102+
int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
103+
size_t cpusetsize, unsigned long *cpus,
104+
unsigned int flags);
105+
106+
int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
107+
size_t cpusetsize, unsigned long *cpus,
108+
unsigned int flags)
109+
{
110+
if (flags & RISCV_HWPROBE_WHICH_CPUS)
111+
return riscv_vdso_get_cpus(pairs, pair_count, cpusetsize,
112+
cpus, flags);
113+
114+
return riscv_vdso_get_values(pairs, pair_count, cpusetsize,
115+
cpus, flags);
116+
}

0 commit comments

Comments
 (0)