Re: [RFC PATCH 4/5] RISC-V: selftests: Add which-cpus hwprobe test

Palmer Dabbelt
2023-09-25T11:23:49Z

On Thu, 21 Sep 2023 05:55:23 PDT (-0700), ajones@ventanamicro.com wrote:
> Test the RISCV_HWPROBE_WHICH_CPUS flag of hwprobe. The test also
> has a command line interface in order to get the cpu list for
> arbitrary hwprobe pairs.
>
> Signed-off-by: Andrew Jones <ajones@ventanamicro.com>
> ---
> .../testing/selftests/riscv/hwprobe/Makefile | 5 +-
> .../selftests/riscv/hwprobe/which-cpus.c | 156 ++++++++++++++++++
> 2 files changed, 160 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/riscv/hwprobe/which-cpus.c
>
> diff --git a/tools/testing/selftests/riscv/hwprobe/Makefile b/tools/testing/selftests/riscv/hwprobe/Makefile
> index f224b84591fb..cec81610a5f2 100644
> --- a/tools/testing/selftests/riscv/hwprobe/Makefile
> +++ b/tools/testing/selftests/riscv/hwprobe/Makefile
> @@ -4,7 +4,7 @@
>
> CFLAGS += -I$(top_srcdir)/tools/include
>
> -TEST_GEN_PROGS := hwprobe cbo
> +TEST_GEN_PROGS := hwprobe cbo which-cpus
>
> include ../../lib.mk
>
> @@ -13,3 +13,6 @@ $(OUTPUT)/hwprobe: hwprobe.c sys_hwprobe.S
>
> $(OUTPUT)/cbo: cbo.c sys_hwprobe.S
> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> +
> +$(OUTPUT)/which-cpus: which-cpus.c sys_hwprobe.S
> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> diff --git a/tools/testing/selftests/riscv/hwprobe/which-cpus.c b/tools/testing/selftests/riscv/hwprobe/which-cpus.c
> new file mode 100644
> index 000000000000..83877a715b08
> --- /dev/null
> +++ b/tools/testing/selftests/riscv/hwprobe/which-cpus.c
> @@ -0,0 +1,156 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2023 Ventana Micro Systems Inc.
> + *
> + * Test the RISCV_HWPROBE_WHICH_CPUS flag of hwprobe. Also provides a command
> + * line interface to get the cpu list for arbitrary hwprobe pairs.
> + */
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sched.h>
> +#include <unistd.h>
> +#include <assert.h>
> +
> +#include "hwprobe.h"
> +#include "../../kselftest.h"
> +
> +static void help(void)
> +{
> + printf("\n"
> + "which-cpus: [-h] [<key=value> [<key=value> ...]]\n\n"
> + " Without parameters, tests the RISCV_HWPROBE_WHICH_CPUS flag of hwprobe.\n"
> + " With parameters, where each parameter is a hwprobe pair written as\n"
> + " <key=value>, outputs the cpulist for cpus which all match the given set\n"
> + " of pairs. 'key' and 'value' should be in numeric form, e.g. 4=0x3b\n");
> +}
> +
> +static void print_cpulist(cpu_set_t *cpus)
> +{
> + int start = 0, end = 0;
> +
> + if (!CPU_COUNT(cpus)) {
> + printf("cpus: None\n");
> + return;
> + }
> +
> + printf("cpus:");
> + for (int i = 0, c = 0; i < CPU_COUNT(cpus); i++, c++) {
> + if (start != end && !CPU_ISSET(c, cpus))
> + printf("-%d", end);
> +
> + while (!CPU_ISSET(c, cpus))
> + ++c;
> +
> + if (i != 0 && c == end + 1) {
> + end = c;
> + continue;
> + }
> +
> + printf("%c%d", i == 0 ? ' ' : ',', c);
> + start = end = c;
> + }
> + if (start != end)
> + printf("-%d", end);
> + printf("\n");
> +}
> +
> +static void do_which_cpus(int argc, char **argv)
> +{
> + struct riscv_hwprobe *pairs;
> + int nr_pairs = argc - 1;
> + cpu_set_t cpus;
> + char *start, *end;
> + int rc;
> +
> + pairs = malloc(nr_pairs * sizeof(struct riscv_hwprobe));
> + assert(pairs);
> +
> + for (int i = 0; i < nr_pairs; i++) {
> + start = argv[i + 1];
> + pairs[i].key = strtol(start, &end, 0);
> + assert(end != start && *end == '=');
> + start = end + 1;
> + pairs[i].value = strtoul(start, &end, 0);
> + assert(end != start && *end == '\0');
> + }
> +
> + CPU_ZERO(&cpus);
> + rc = riscv_hwprobe(pairs, nr_pairs, sizeof(cpu_set_t), (unsigned long *)&cpus, RISCV_HWPROBE_WHICH_CPUS);
> + assert(rc == 0);
> + print_cpulist(&cpus);
> + free(pairs);
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct riscv_hwprobe pairs[2];
> + cpu_set_t cpus_aff, cpus;
> + __u64 ext0_all;
> + long rc;
> +
> + if (argc > 1) {
> + if (!strcmp(argv[1], "-h"))
> + help();
> + else
> + do_which_cpus(argc, argv);
> + return 0;
> + }
> +
> + rc = sched_getaffinity(0, sizeof(cpu_set_t), &cpus_aff);
> + assert(rc == 0);
> +
> + ksft_print_header();
> + ksft_set_plan(7);
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR, };
> + rc = riscv_hwprobe(pairs, 1, 0, NULL, 0);
> + assert(rc == 0 && pairs[0].key == RISCV_HWPROBE_KEY_BASE_BEHAVIOR &&
> + pairs[0].value == RISCV_HWPROBE_BASE_BEHAVIOR_IMA);
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_IMA_EXT_0, };
> + rc = riscv_hwprobe(pairs, 1, 0, NULL, 0);
> + assert(rc == 0 && pairs[0].key == RISCV_HWPROBE_KEY_IMA_EXT_0);
> + ext0_all = pairs[0].value;
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR, .value = RISCV_HWPROBE_BASE_BEHAVIOR_IMA, };
> + CPU_ZERO(&cpus);
> + rc = riscv_hwprobe(pairs, 1, 0, (unsigned long *)&cpus, RISCV_HWPROBE_WHICH_CPUS);
> + ksft_test_result(rc == -EINVAL, "no cpusetsize\n");
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR, .value = RISCV_HWPROBE_BASE_BEHAVIOR_IMA, };
> + rc = riscv_hwprobe(pairs, 1, sizeof(cpu_set_t), NULL, RISCV_HWPROBE_WHICH_CPUS);
> + ksft_test_result(rc == -EINVAL, "NULL cpus\n");
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = 0xbadc0de, };
> + CPU_ZERO(&cpus);
> + rc = riscv_hwprobe(pairs, 1, sizeof(cpu_set_t), (unsigned long *)&cpus, RISCV_HWPROBE_WHICH_CPUS);
> + ksft_test_result(rc == 0 && CPU_COUNT(&cpus) == 0, "unknown key\n");
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR, .value = RISCV_HWPROBE_BASE_BEHAVIOR_IMA, };
> + pairs[1] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR, .value = RISCV_HWPROBE_BASE_BEHAVIOR_IMA, };
> + CPU_ZERO(&cpus);
> + rc = riscv_hwprobe(pairs, 2, sizeof(cpu_set_t), (unsigned long *)&cpus, RISCV_HWPROBE_WHICH_CPUS);
> + ksft_test_result(rc == 0, "duplicate keys\n");
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR, .value = RISCV_HWPROBE_BASE_BEHAVIOR_IMA, };
> + pairs[1] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_IMA_EXT_0, .value = ext0_all, };
> + CPU_ZERO(&cpus);
> + rc = riscv_hwprobe(pairs, 2, sizeof(cpu_set_t), (unsigned long *)&cpus, RISCV_HWPROBE_WHICH_CPUS);
> + ksft_test_result(rc == 0 && CPU_COUNT(&cpus) == sysconf(_SC_NPROCESSORS_ONLN), "set all cpus\n");
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR, .value = RISCV_HWPROBE_BASE_BEHAVIOR_IMA, };
> + pairs[1] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_IMA_EXT_0, .value = ext0_all, };
> + memcpy(&cpus, &cpus_aff, sizeof(cpu_set_t));
> + rc = riscv_hwprobe(pairs, 2, sizeof(cpu_set_t), (unsigned long *)&cpus, RISCV_HWPROBE_WHICH_CPUS);
> + ksft_test_result(rc == 0 && CPU_EQUAL(&cpus, &cpus_aff), "set all affinity cpus\n");
> +
> + pairs[0] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR, .value = RISCV_HWPROBE_BASE_BEHAVIOR_IMA, };
> + pairs[1] = (struct riscv_hwprobe){ .key = RISCV_HWPROBE_KEY_IMA_EXT_0, .value = ~ext0_all, };
> + memcpy(&cpus, &cpus_aff, sizeof(cpu_set_t));
> + rc = riscv_hwprobe(pairs, 2, sizeof(cpu_set_t), (unsigned long *)&cpus, RISCV_HWPROBE_WHICH_CPUS);
> + ksft_test_result(rc == 0 && CPU_COUNT(&cpus) == 0, "clear all cpus\n");
> +
> + ksft_finished();
> +}

Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com>