]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
kselftest/arm64: signal: fix/refactor SVE vector length enumeration
authorAndre Przywara <andre.przywara@arm.com>
Wed, 21 Aug 2024 16:44:01 +0000 (17:44 +0100)
committerWill Deacon <will@kernel.org>
Fri, 23 Aug 2024 10:34:55 +0000 (11:34 +0100)
Currently a number of SVE/SME related tests have almost identical
functions to enumerate all supported vector lengths. However over time
the copy&pasted code has diverged, allowing some bugs to creep in:
- fake_sigreturn_sme_change_vl reports a failure, not a SKIP if only
  one vector length is supported (but the SVE version is fine)
- fake_sigreturn_sme_change_vl tries to set the SVE vector length, not
  the SME one (but the other SME tests are fine)
- za_no_regs keeps iterating forever if only one vector length is
  supported (but za_regs is correct)

Since those bugs seem to be mostly copy&paste ones, let's consolidate
the enumeration loop into one shared function, and just call that from
each test. That should fix the above bugs, and prevent similar issues
from happening again.

Fixes: 4963aeb35a9e ("kselftest/arm64: signal: Add SME signal handling tests")
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20240821164401.3598545-1-andre.przywara@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
tools/testing/selftests/arm64/signal/Makefile
tools/testing/selftests/arm64/signal/sve_helpers.c [new file with mode: 0644]
tools/testing/selftests/arm64/signal/sve_helpers.h [new file with mode: 0644]
tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_sme_change_vl.c
tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_sve_change_vl.c
tools/testing/selftests/arm64/signal/testcases/ssve_regs.c
tools/testing/selftests/arm64/signal/testcases/ssve_za_regs.c
tools/testing/selftests/arm64/signal/testcases/sve_regs.c
tools/testing/selftests/arm64/signal/testcases/za_no_regs.c
tools/testing/selftests/arm64/signal/testcases/za_regs.c

index 8f5febaf1a9a257ea0dd41f0ebb762c0a36e7719..edb3613513b8a8aecde51fdf5c1c28745a54fa2e 100644 (file)
@@ -23,7 +23,7 @@ $(TEST_GEN_PROGS): $(PROGS)
 # Common test-unit targets to build common-layout test-cases executables
 # Needs secondary expansion to properly include the testcase c-file in pre-reqs
 COMMON_SOURCES := test_signals.c test_signals_utils.c testcases/testcases.c \
-       signals.S
+       signals.S sve_helpers.c
 COMMON_HEADERS := test_signals.h test_signals_utils.h testcases/testcases.h
 
 .SECONDEXPANSION:
diff --git a/tools/testing/selftests/arm64/signal/sve_helpers.c b/tools/testing/selftests/arm64/signal/sve_helpers.c
new file mode 100644 (file)
index 0000000..0acc121
--- /dev/null
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 ARM Limited
+ *
+ * Common helper functions for SVE and SME functionality.
+ */
+
+#include <stdbool.h>
+#include <kselftest.h>
+#include <asm/sigcontext.h>
+#include <sys/prctl.h>
+
+unsigned int vls[SVE_VQ_MAX];
+unsigned int nvls;
+
+int sve_fill_vls(bool use_sme, int min_vls)
+{
+       int vq, vl;
+       int pr_set_vl = use_sme ? PR_SME_SET_VL : PR_SVE_SET_VL;
+       int len_mask = use_sme ? PR_SME_VL_LEN_MASK : PR_SVE_VL_LEN_MASK;
+
+       /*
+        * Enumerate up to SVE_VQ_MAX vector lengths
+        */
+       for (vq = SVE_VQ_MAX; vq > 0; --vq) {
+               vl = prctl(pr_set_vl, vq * 16);
+               if (vl == -1)
+                       return KSFT_FAIL;
+
+               vl &= len_mask;
+
+               /*
+                * Unlike SVE, SME does not require the minimum vector length
+                * to be implemented, or the VLs to be consecutive, so any call
+                * to the prctl might return the single implemented VL, which
+                * might be larger than 16. So to avoid this loop never
+                * terminating,  bail out here when we find a higher VL than
+                * we asked for.
+                * See the ARM ARM, DDI 0487K.a, B1.4.2: I_QQRNR and I_NWYBP.
+                */
+               if (vq < sve_vq_from_vl(vl))
+                       break;
+
+               /* Skip missing VLs */
+               vq = sve_vq_from_vl(vl);
+
+               vls[nvls++] = vl;
+       }
+
+       if (nvls < min_vls) {
+               fprintf(stderr, "Only %d VL supported\n", nvls);
+               return KSFT_SKIP;
+       }
+
+       return KSFT_PASS;
+}
diff --git a/tools/testing/selftests/arm64/signal/sve_helpers.h b/tools/testing/selftests/arm64/signal/sve_helpers.h
new file mode 100644 (file)
index 0000000..50948ce
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024 ARM Limited
+ *
+ * Common helper functions for SVE and SME functionality.
+ */
+
+#ifndef __SVE_HELPERS_H__
+#define __SVE_HELPERS_H__
+
+#include <stdbool.h>
+
+#define VLS_USE_SVE    false
+#define VLS_USE_SME    true
+
+extern unsigned int vls[];
+extern unsigned int nvls;
+
+int sve_fill_vls(bool use_sme, int min_vls);
+
+#endif
index ebd5815b54bbaaf883ddeed306260b00180f5b0e..cb8c051b5c8f2ea31c6c8d05aea1247ec50c983f 100644 (file)
@@ -6,44 +6,28 @@
  * handler, this is not supported and is expected to segfault.
  */
 
+#include <kselftest.h>
 #include <signal.h>
 #include <ucontext.h>
 #include <sys/prctl.h>
 
 #include "test_signals_utils.h"
+#include "sve_helpers.h"
 #include "testcases.h"
 
 struct fake_sigframe sf;
-static unsigned int vls[SVE_VQ_MAX];
-unsigned int nvls = 0;
 
 static bool sme_get_vls(struct tdescr *td)
 {
-       int vq, vl;
+       int res = sve_fill_vls(VLS_USE_SME, 2);
 
-       /*
-        * Enumerate up to SVE_VQ_MAX vector lengths
-        */
-       for (vq = SVE_VQ_MAX; vq > 0; --vq) {
-               vl = prctl(PR_SVE_SET_VL, vq * 16);
-               if (vl == -1)
-                       return false;
+       if (!res)
+               return true;
 
-               vl &= PR_SME_VL_LEN_MASK;
+       if (res == KSFT_SKIP)
+               td->result = KSFT_SKIP;
 
-               /* Skip missing VLs */
-               vq = sve_vq_from_vl(vl);
-
-               vls[nvls++] = vl;
-       }
-
-       /* We need at least two VLs */
-       if (nvls < 2) {
-               fprintf(stderr, "Only %d VL supported\n", nvls);
-               return false;
-       }
-
-       return true;
+       return false;
 }
 
 static int fake_sigreturn_ssve_change_vl(struct tdescr *td,
index e2a452190511ff4ab7e15c084347b5bdb7b967fe..e1ccf8f85a70c8878791b47bd3a48713a36f2aba 100644 (file)
 #include <sys/prctl.h>
 
 #include "test_signals_utils.h"
+#include "sve_helpers.h"
 #include "testcases.h"
 
 struct fake_sigframe sf;
-static unsigned int vls[SVE_VQ_MAX];
-unsigned int nvls = 0;
 
 static bool sve_get_vls(struct tdescr *td)
 {
-       int vq, vl;
+       int res = sve_fill_vls(VLS_USE_SVE, 2);
 
-       /*
-        * Enumerate up to SVE_VQ_MAX vector lengths
-        */
-       for (vq = SVE_VQ_MAX; vq > 0; --vq) {
-               vl = prctl(PR_SVE_SET_VL, vq * 16);
-               if (vl == -1)
-                       return false;
+       if (!res)
+               return true;
 
-               vl &= PR_SVE_VL_LEN_MASK;
-
-               /* Skip missing VLs */
-               vq = sve_vq_from_vl(vl);
-
-               vls[nvls++] = vl;
-       }
-
-       /* We need at least two VLs */
-       if (nvls < 2) {
-               fprintf(stderr, "Only %d VL supported\n", nvls);
+       if (res == KSFT_SKIP)
                td->result = KSFT_SKIP;
-               return false;
-       }
 
-       return true;
+       return false;
 }
 
 static int fake_sigreturn_sve_change_vl(struct tdescr *td,
index 3d37daafcff5131e722c8d30f9e3c144f8346855..6dbe48cf8b09ed8b7a5ab47690bd87e39e18e1e6 100644 (file)
@@ -6,51 +6,31 @@
  * set up as expected.
  */
 
+#include <kselftest.h>
 #include <signal.h>
 #include <ucontext.h>
 #include <sys/prctl.h>
 
 #include "test_signals_utils.h"
+#include "sve_helpers.h"
 #include "testcases.h"
 
 static union {
        ucontext_t uc;
        char buf[1024 * 64];
 } context;
-static unsigned int vls[SVE_VQ_MAX];
-unsigned int nvls = 0;
 
 static bool sme_get_vls(struct tdescr *td)
 {
-       int vq, vl;
+       int res = sve_fill_vls(VLS_USE_SME, 1);
 
-       /*
-        * Enumerate up to SVE_VQ_MAX vector lengths
-        */
-       for (vq = SVE_VQ_MAX; vq > 0; --vq) {
-               vl = prctl(PR_SME_SET_VL, vq * 16);
-               if (vl == -1)
-                       return false;
-
-               vl &= PR_SME_VL_LEN_MASK;
-
-               /* Did we find the lowest supported VL? */
-               if (vq < sve_vq_from_vl(vl))
-                       break;
+       if (!res)
+               return true;
 
-               /* Skip missing VLs */
-               vq = sve_vq_from_vl(vl);
-
-               vls[nvls++] = vl;
-       }
-
-       /* We need at least one VL */
-       if (nvls < 1) {
-               fprintf(stderr, "Only %d VL supported\n", nvls);
-               return false;
-       }
+       if (res == KSFT_SKIP)
+               td->result = KSFT_SKIP;
 
-       return true;
+       return false;
 }
 
 static void setup_ssve_regs(void)
index 9dc5f128bbc0d525362eee5d04e66c6e64c108b3..5557e116e97363247250de85e10522d8400d65cf 100644 (file)
@@ -6,51 +6,31 @@
  * signal frames is set up as expected when enabled simultaneously.
  */
 
+#include <kselftest.h>
 #include <signal.h>
 #include <ucontext.h>
 #include <sys/prctl.h>
 
 #include "test_signals_utils.h"
+#include "sve_helpers.h"
 #include "testcases.h"
 
 static union {
        ucontext_t uc;
        char buf[1024 * 128];
 } context;
-static unsigned int vls[SVE_VQ_MAX];
-unsigned int nvls = 0;
 
 static bool sme_get_vls(struct tdescr *td)
 {
-       int vq, vl;
+       int res = sve_fill_vls(VLS_USE_SME, 1);
 
-       /*
-        * Enumerate up to SVE_VQ_MAX vector lengths
-        */
-       for (vq = SVE_VQ_MAX; vq > 0; --vq) {
-               vl = prctl(PR_SME_SET_VL, vq * 16);
-               if (vl == -1)
-                       return false;
-
-               vl &= PR_SME_VL_LEN_MASK;
-
-               /* Did we find the lowest supported VL? */
-               if (vq < sve_vq_from_vl(vl))
-                       break;
+       if (!res)
+               return true;
 
-               /* Skip missing VLs */
-               vq = sve_vq_from_vl(vl);
-
-               vls[nvls++] = vl;
-       }
-
-       /* We need at least one VL */
-       if (nvls < 1) {
-               fprintf(stderr, "Only %d VL supported\n", nvls);
-               return false;
-       }
+       if (res == KSFT_SKIP)
+               td->result = KSFT_SKIP;
 
-       return true;
+       return false;
 }
 
 static void setup_regs(void)
index 8b16eabbb7697e3cb13e3b9d747345bb12b26bb6..8143eb1c58c187a8411638386cd2e1e40a0413f0 100644 (file)
@@ -6,47 +6,31 @@
  * expected.
  */
 
+#include <kselftest.h>
 #include <signal.h>
 #include <ucontext.h>
 #include <sys/prctl.h>
 
 #include "test_signals_utils.h"
+#include "sve_helpers.h"
 #include "testcases.h"
 
 static union {
        ucontext_t uc;
        char buf[1024 * 64];
 } context;
-static unsigned int vls[SVE_VQ_MAX];
-unsigned int nvls = 0;
 
 static bool sve_get_vls(struct tdescr *td)
 {
-       int vq, vl;
+       int res = sve_fill_vls(VLS_USE_SVE, 1);
 
-       /*
-        * Enumerate up to SVE_VQ_MAX vector lengths
-        */
-       for (vq = SVE_VQ_MAX; vq > 0; --vq) {
-               vl = prctl(PR_SVE_SET_VL, vq * 16);
-               if (vl == -1)
-                       return false;
-
-               vl &= PR_SVE_VL_LEN_MASK;
-
-               /* Skip missing VLs */
-               vq = sve_vq_from_vl(vl);
+       if (!res)
+               return true;
 
-               vls[nvls++] = vl;
-       }
-
-       /* We need at least one VL */
-       if (nvls < 1) {
-               fprintf(stderr, "Only %d VL supported\n", nvls);
-               return false;
-       }
+       if (res == KSFT_SKIP)
+               td->result = KSFT_SKIP;
 
-       return true;
+       return false;
 }
 
 static void setup_sve_regs(void)
index 4d6f94b6178f36eb56677f0b8c4d8b7873e70832..ce26e9c2fa5e34ab28006ac8b2937a1e3051aa08 100644 (file)
@@ -6,47 +6,31 @@
  * expected.
  */
 
+#include <kselftest.h>
 #include <signal.h>
 #include <ucontext.h>
 #include <sys/prctl.h>
 
 #include "test_signals_utils.h"
+#include "sve_helpers.h"
 #include "testcases.h"
 
 static union {
        ucontext_t uc;
        char buf[1024 * 128];
 } context;
-static unsigned int vls[SVE_VQ_MAX];
-unsigned int nvls = 0;
 
 static bool sme_get_vls(struct tdescr *td)
 {
-       int vq, vl;
+       int res = sve_fill_vls(VLS_USE_SME, 1);
 
-       /*
-        * Enumerate up to SME_VQ_MAX vector lengths
-        */
-       for (vq = SVE_VQ_MAX; vq > 0; --vq) {
-               vl = prctl(PR_SME_SET_VL, vq * 16);
-               if (vl == -1)
-                       return false;
-
-               vl &= PR_SME_VL_LEN_MASK;
-
-               /* Skip missing VLs */
-               vq = sve_vq_from_vl(vl);
+       if (!res)
+               return true;
 
-               vls[nvls++] = vl;
-       }
-
-       /* We need at least one VL */
-       if (nvls < 1) {
-               fprintf(stderr, "Only %d VL supported\n", nvls);
-               return false;
-       }
+       if (res == KSFT_SKIP)
+               td->result = KSFT_SKIP;
 
-       return true;
+       return false;
 }
 
 static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc,
index 174ad665669647042e4dc7e040ec8d852b55cd62..b9e13f27f1f9aaf55db2a5e391f360993561d0b7 100644 (file)
@@ -6,51 +6,31 @@
  * expected.
  */
 
+#include <kselftest.h>
 #include <signal.h>
 #include <ucontext.h>
 #include <sys/prctl.h>
 
 #include "test_signals_utils.h"
+#include "sve_helpers.h"
 #include "testcases.h"
 
 static union {
        ucontext_t uc;
        char buf[1024 * 128];
 } context;
-static unsigned int vls[SVE_VQ_MAX];
-unsigned int nvls = 0;
 
 static bool sme_get_vls(struct tdescr *td)
 {
-       int vq, vl;
+       int res = sve_fill_vls(VLS_USE_SME, 1);
 
-       /*
-        * Enumerate up to SME_VQ_MAX vector lengths
-        */
-       for (vq = SVE_VQ_MAX; vq > 0; --vq) {
-               vl = prctl(PR_SME_SET_VL, vq * 16);
-               if (vl == -1)
-                       return false;
-
-               vl &= PR_SME_VL_LEN_MASK;
-
-               /* Did we find the lowest supported VL? */
-               if (vq < sve_vq_from_vl(vl))
-                       break;
+       if (!res)
+               return true;
 
-               /* Skip missing VLs */
-               vq = sve_vq_from_vl(vl);
-
-               vls[nvls++] = vl;
-       }
-
-       /* We need at least one VL */
-       if (nvls < 1) {
-               fprintf(stderr, "Only %d VL supported\n", nvls);
-               return false;
-       }
+       if (res == KSFT_SKIP)
+               td->result = KSFT_SKIP;
 
-       return true;
+       return false;
 }
 
 static void setup_za_regs(void)