Skip to content

Commit 0c7c237

Browse files
brooniewilldeacon
authored andcommitted
kselftest/arm64: Add a test case for SVE VL changes with SME active
We just fixed an issue where changing the SVE VL while SME was active could result in us attempting to save the streaming mode SVE vectors without any backing storage. Add a test case which provokes that issue, ideally we should also verify that the contents of ZA are unaffected by any of what we did. Note that since we need to keep streaming mode enabled we can't use any syscalls to trigger the issue, we have to sit in a loop in usersapce and hope to be preempted. The chosen numbers trigger with defconfig on all the virtual platforms for me, this won't be 100% on all systems but avoid an overcomplicated test implementation. Signed-off-by: Mark Brown <[email protected]> Link: https://lore.kernel.org/r/20230720-arm64-fix-sve-sme-vl-change-v2-2-8eea06b82d57@kernel.org Signed-off-by: Will Deacon <[email protected]>
1 parent 6eaae19 commit 0c7c237

File tree

1 file changed

+102
-3
lines changed

1 file changed

+102
-3
lines changed

tools/testing/selftests/arm64/fp/vec-syscfg.c

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <assert.h>
77
#include <errno.h>
88
#include <fcntl.h>
9+
#include <stdbool.h>
910
#include <stddef.h>
1011
#include <stdio.h>
1112
#include <stdlib.h>
@@ -39,9 +40,11 @@ struct vec_data {
3940
int max_vl;
4041
};
4142

43+
#define VEC_SVE 0
44+
#define VEC_SME 1
4245

4346
static struct vec_data vec_data[] = {
44-
{
47+
[VEC_SVE] = {
4548
.name = "SVE",
4649
.hwcap_type = AT_HWCAP,
4750
.hwcap = HWCAP_SVE,
@@ -51,7 +54,7 @@ static struct vec_data vec_data[] = {
5154
.prctl_set = PR_SVE_SET_VL,
5255
.default_vl_file = "/proc/sys/abi/sve_default_vector_length",
5356
},
54-
{
57+
[VEC_SME] = {
5558
.name = "SME",
5659
.hwcap_type = AT_HWCAP2,
5760
.hwcap = HWCAP2_SME,
@@ -644,18 +647,107 @@ static const test_type tests[] = {
644647
prctl_set_all_vqs,
645648
};
646649

650+
static inline void smstart(void)
651+
{
652+
asm volatile("msr S0_3_C4_C7_3, xzr");
653+
}
654+
655+
static inline void smstart_sm(void)
656+
{
657+
asm volatile("msr S0_3_C4_C3_3, xzr");
658+
}
659+
660+
static inline void smstop(void)
661+
{
662+
asm volatile("msr S0_3_C4_C6_3, xzr");
663+
}
664+
665+
666+
/*
667+
* Verify we can change the SVE vector length while SME is active and
668+
* continue to use SME afterwards.
669+
*/
670+
static void change_sve_with_za(void)
671+
{
672+
struct vec_data *sve_data = &vec_data[VEC_SVE];
673+
bool pass = true;
674+
int ret, i;
675+
676+
if (sve_data->min_vl == sve_data->max_vl) {
677+
ksft_print_msg("Only one SVE VL supported, can't change\n");
678+
ksft_test_result_skip("change_sve_while_sme\n");
679+
return;
680+
}
681+
682+
/* Ensure we will trigger a change when we set the maximum */
683+
ret = prctl(sve_data->prctl_set, sve_data->min_vl);
684+
if (ret != sve_data->min_vl) {
685+
ksft_print_msg("Failed to set SVE VL %d: %d\n",
686+
sve_data->min_vl, ret);
687+
pass = false;
688+
}
689+
690+
/* Enable SM and ZA */
691+
smstart();
692+
693+
/* Trigger another VL change */
694+
ret = prctl(sve_data->prctl_set, sve_data->max_vl);
695+
if (ret != sve_data->max_vl) {
696+
ksft_print_msg("Failed to set SVE VL %d: %d\n",
697+
sve_data->max_vl, ret);
698+
pass = false;
699+
}
700+
701+
/*
702+
* Spin for a bit with SM enabled to try to trigger another
703+
* save/restore. We can't use syscalls without exiting
704+
* streaming mode.
705+
*/
706+
for (i = 0; i < 100000000; i++)
707+
smstart_sm();
708+
709+
/*
710+
* TODO: Verify that ZA was preserved over the VL change and
711+
* spin.
712+
*/
713+
714+
/* Clean up after ourselves */
715+
smstop();
716+
ret = prctl(sve_data->prctl_set, sve_data->default_vl);
717+
if (ret != sve_data->default_vl) {
718+
ksft_print_msg("Failed to restore SVE VL %d: %d\n",
719+
sve_data->default_vl, ret);
720+
pass = false;
721+
}
722+
723+
ksft_test_result(pass, "change_sve_with_za\n");
724+
}
725+
726+
typedef void (*test_all_type)(void);
727+
728+
static const struct {
729+
const char *name;
730+
test_all_type test;
731+
} all_types_tests[] = {
732+
{ "change_sve_with_za", change_sve_with_za },
733+
};
734+
647735
int main(void)
648736
{
737+
bool all_supported = true;
649738
int i, j;
650739

651740
ksft_print_header();
652-
ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data));
741+
ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data) +
742+
ARRAY_SIZE(all_types_tests));
653743

654744
for (i = 0; i < ARRAY_SIZE(vec_data); i++) {
655745
struct vec_data *data = &vec_data[i];
656746
unsigned long supported;
657747

658748
supported = getauxval(data->hwcap_type) & data->hwcap;
749+
if (!supported)
750+
all_supported = false;
659751

660752
for (j = 0; j < ARRAY_SIZE(tests); j++) {
661753
if (supported)
@@ -666,5 +758,12 @@ int main(void)
666758
}
667759
}
668760

761+
for (i = 0; i < ARRAY_SIZE(all_types_tests); i++) {
762+
if (all_supported)
763+
all_types_tests[i].test();
764+
else
765+
ksft_test_result_skip("%s\n", all_types_tests[i].name);
766+
}
767+
669768
ksft_exit_pass();
670769
}

0 commit comments

Comments
 (0)