]> www.infradead.org Git - users/hch/block.git/commitdiff
RISC-V: hwprobe: Introduce which-cpus flag
authorAndrew Jones <ajones@ventanamicro.com>
Wed, 22 Nov 2023 16:47:04 +0000 (17:47 +0100)
committerPalmer Dabbelt <palmer@rivosinc.com>
Wed, 3 Jan 2024 11:36:49 +0000 (03:36 -0800)
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>
Documentation/arch/riscv/hwprobe.rst
arch/riscv/include/asm/hwprobe.h
arch/riscv/include/uapi/asm/hwprobe.h
arch/riscv/kernel/sys_hwprobe.c
arch/riscv/kernel/vdso/hwprobe.c

index 132e9acaa8f45a8337b89512ff2398191275ceb1..12f9b0a043ac08cab2de4a29f838b6a9b6c395c8 100644 (file)
@@ -25,8 +25,21 @@ arch, impl), the returned value will only be valid if all CPUs in the given set
 have the same value. Otherwise -1 will be returned. For boolean-like keys, the
 value returned will be a logical AND of the values for the specified CPUs.
 Usermode can supply NULL for ``cpus`` and 0 for ``cpusetsize`` as a shortcut for
-all online CPUs. There are currently no flags, this value must be zero for
-future compatibility.
+all online CPUs. The currently supported flags are:
+
+* :c:macro:`RISCV_HWPROBE_WHICH_CPUS`: This flag basically reverses the behavior
+  of sys_riscv_hwprobe().  Instead of populating the values of keys for a given
+  set of CPUs, the values of each key are given and the set of CPUs is reduced
+  by sys_riscv_hwprobe() to only those which match each of the key-value pairs.
+  How matching is done depends on the key type.  For value-like keys, matching
+  means to be the exact same as the value.  For boolean-like keys, matching
+  means the result of a logical AND of the pair's value with the CPU's value is
+  exactly the same as the pair's value.  Additionally, when ``cpus`` is an empty
+  set, then it is initialized to all online CPUs which fit within it, i.e. the
+  CPU set returned is the reduction of all the online CPUs which can be
+  represented with a CPU set of size ``cpusetsize``.
+
+All other flags are reserved for future compatibility and must be zero.
 
 On success 0 is returned, on failure a negative error code is returned.
 
index 5c48f48e79a67823707806a3e7872e1ed6f3388c..630507dff5ead30a368fcae56489c5ccb988e3e7 100644 (file)
@@ -15,4 +15,28 @@ static inline bool riscv_hwprobe_key_is_valid(__s64 key)
        return key >= 0 && key <= RISCV_HWPROBE_MAX_KEY;
 }
 
+static inline bool hwprobe_key_is_bitmask(__s64 key)
+{
+       switch (key) {
+       case RISCV_HWPROBE_KEY_BASE_BEHAVIOR:
+       case RISCV_HWPROBE_KEY_IMA_EXT_0:
+       case RISCV_HWPROBE_KEY_CPUPERF_0:
+               return true;
+       }
+
+       return false;
+}
+
+static inline bool riscv_hwprobe_pair_cmp(struct riscv_hwprobe *pair,
+                                         struct riscv_hwprobe *other_pair)
+{
+       if (pair->key != other_pair->key)
+               return false;
+
+       if (hwprobe_key_is_bitmask(pair->key))
+               return (pair->value & other_pair->value) == other_pair->value;
+
+       return pair->value == other_pair->value;
+}
+
 #endif
index b659ffcfcdb454cf4d12cd513bfcb23cf32b11c5..7aa7d5c71e7905dbb99bd3e12cbffaa35bb1d4cd 100644 (file)
@@ -40,4 +40,7 @@ struct riscv_hwprobe {
 #define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE    6
 /* Increase RISCV_HWPROBE_MAX_KEY when adding items. */
 
+/* Flags */
+#define RISCV_HWPROBE_WHICH_CPUS       (1 << 0)
+
 #endif
index c0ccf1f45aaac93c5eb246a57bfdb1b28ed0e780..b7cfb26ce31c9b5c097f5aa9d492a00a7ebea27f 100644 (file)
@@ -179,10 +179,10 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair,
        }
 }
 
-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)
+static int hwprobe_get_values(struct riscv_hwprobe __user *pairs,
+                             size_t pair_count, size_t cpusetsize,
+                             unsigned long __user *cpus_user,
+                             unsigned int flags)
 {
        size_t out;
        int ret;
@@ -236,6 +236,92 @@ static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
        return 0;
 }
 
+static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
+                           size_t pair_count, size_t cpusetsize,
+                           unsigned long __user *cpus_user,
+                           unsigned int flags)
+{
+       cpumask_t cpus, one_cpu;
+       bool clear_all = false;
+       size_t i;
+       int ret;
+
+       if (flags != RISCV_HWPROBE_WHICH_CPUS)
+               return -EINVAL;
+
+       if (!cpusetsize || !cpus_user)
+               return -EINVAL;
+
+       if (cpusetsize > cpumask_size())
+               cpusetsize = cpumask_size();
+
+       ret = copy_from_user(&cpus, cpus_user, cpusetsize);
+       if (ret)
+               return -EFAULT;
+
+       if (cpumask_empty(&cpus))
+               cpumask_copy(&cpus, cpu_online_mask);
+
+       cpumask_and(&cpus, &cpus, cpu_online_mask);
+
+       cpumask_clear(&one_cpu);
+
+       for (i = 0; i < pair_count; i++) {
+               struct riscv_hwprobe pair, tmp;
+               int cpu;
+
+               ret = copy_from_user(&pair, &pairs[i], sizeof(pair));
+               if (ret)
+                       return -EFAULT;
+
+               if (!riscv_hwprobe_key_is_valid(pair.key)) {
+                       clear_all = true;
+                       pair = (struct riscv_hwprobe){ .key = -1, };
+                       ret = copy_to_user(&pairs[i], &pair, sizeof(pair));
+                       if (ret)
+                               return -EFAULT;
+               }
+
+               if (clear_all)
+                       continue;
+
+               tmp = (struct riscv_hwprobe){ .key = pair.key, };
+
+               for_each_cpu(cpu, &cpus) {
+                       cpumask_set_cpu(cpu, &one_cpu);
+
+                       hwprobe_one_pair(&tmp, &one_cpu);
+
+                       if (!riscv_hwprobe_pair_cmp(&tmp, &pair))
+                               cpumask_clear_cpu(cpu, &cpus);
+
+                       cpumask_clear_cpu(cpu, &one_cpu);
+               }
+       }
+
+       if (clear_all)
+               cpumask_clear(&cpus);
+
+       ret = copy_to_user(cpus_user, &cpus, cpusetsize);
+       if (ret)
+               return -EFAULT;
+
+       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);
+
+       return hwprobe_get_values(pairs, pair_count, cpusetsize,
+                                 cpus_user, flags);
+}
+
 #ifdef CONFIG_MMU
 
 static int __init init_hwprobe_vdso_data(void)
index 026b7645c5ab05cae0239db7d3a583f8b31147e7..1e926e4b5881b6b2c44ec8438870809539f773c5 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright 2023 Rivos, Inc
  */
 
+#include <linux/string.h>
 #include <linux/types.h>
 #include <vdso/datapage.h>
 #include <vdso/helpers.h>
@@ -11,14 +12,9 @@ extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
                         size_t cpusetsize, unsigned long *cpus,
                         unsigned int flags);
 
-/* 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)
+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_data *vd = __arch_get_vdso_data();
        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,
 
        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_data *vd = __arch_get_vdso_data();
+       const struct arch_vdso_data *avd = &vd->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);
+}