staticint stdio_read_integer(FILE *f, constchar *what, int *val)
{ int n = 0; int ret;
ret = fscanf(f, "%d%*1[\n]%n", val, &n); if (ret < 1 || n < 1) {
ksft_print_msg("failed to parse integer from %s\n", what); return -1;
}
return 0;
}
/* Start a new process and return the vector length it sees */ staticint get_child_rdvl(struct vec_data *data)
{
FILE *out; int pipefd[2];
pid_t pid, child; int read_vl, ret;
ret = pipe(pipefd); if (ret == -1) {
ksft_print_msg("pipe() failed: %d (%s)\n",
errno, strerror(errno)); return -1;
}
/* Child: put vector length on the pipe */ if (child == 0) { /* * Replace stdout with the pipe, errors to stderr from * here as kselftest prints to stdout.
*/
ret = dup2(pipefd[1], 1); if (ret == -1) {
fprintf(stderr, "dup2() %d\n", errno); exit(EXIT_FAILURE);
}
/* exec() a new binary which puts the VL on stdout */
ret = execl(data->rdvl_binary, data->rdvl_binary, NULL);
fprintf(stderr, "execl(%s) failed: %d (%s)\n",
data->rdvl_binary, errno, strerror(errno));
exit(EXIT_FAILURE);
}
close(pipefd[1]);
/* Parent; wait for the exit status from the child & verify it */ do {
pid = wait(&ret); if (pid == -1) {
ksft_print_msg("wait() failed: %d (%s)\n",
errno, strerror(errno));
close(pipefd[0]); return -1;
}
} while (pid != child);
assert(pid == child);
if (!WIFEXITED(ret)) {
ksft_print_msg("child exited abnormally\n");
close(pipefd[0]); return -1;
}
if (WEXITSTATUS(ret) != 0) {
ksft_print_msg("child returned error %d\n",
WEXITSTATUS(ret));
close(pipefd[0]); return -1;
}
out = fdopen(pipefd[0], "r"); if (!out) {
ksft_print_msg("failed to open child stdout\n");
close(pipefd[0]); return -1;
}
ret = stdio_read_integer(out, "child", &read_vl);
fclose(out); if (ret != 0) return ret;
return read_vl;
}
staticint file_read_integer(constchar *name, int *val)
{
FILE *f; int ret;
f = fopen(name, "r"); if (!f) {
ksft_test_result_fail("Unable to open %s: %d (%s)\n",
name, errno,
strerror(errno)); return -1;
}
ret = stdio_read_integer(f, name, val);
fclose(f);
return ret;
}
staticint file_write_integer(constchar *name, int val)
{
FILE *f;
f = fopen(name, "w"); if (!f) {
ksft_test_result_fail("Unable to open %s: %d (%s)\n",
name, errno,
strerror(errno)); return -1;
}
fprintf(f, "%d", val);
fclose(f);
return 0;
}
/* * Verify that we can read the default VL via proc, checking that it * is set in a freshly spawned child.
*/ staticvoid proc_read_default(struct vec_data *data)
{ int default_vl, child_vl, ret;
ret = file_read_integer(data->default_vl_file, &default_vl); if (ret != 0) return;
/* Is this the actual default seen by new processes? */
child_vl = get_child_rdvl(data); if (child_vl != default_vl) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
default_vl, child_vl); return;
}
/* Verify that we can write a minimum value and have it take effect */ staticvoid proc_write_min(struct vec_data *data)
{ int ret, new_default, child_vl;
if (geteuid() != 0) {
ksft_test_result_skip("Need to be root to write to /proc\n"); return;
}
ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL); if (ret != 0) return;
/* What was the new value? */
ret = file_read_integer(data->default_vl_file, &new_default); if (ret != 0) return;
/* Did it take effect in a new process? */
child_vl = get_child_rdvl(data); if (child_vl != new_default) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
new_default, child_vl); return;
}
/* Verify that we can write a maximum value and have it take effect */ staticvoid proc_write_max(struct vec_data *data)
{ int ret, new_default, child_vl;
if (geteuid() != 0) {
ksft_test_result_skip("Need to be root to write to /proc\n"); return;
}
/* -1 is accepted by the /proc interface as the maximum VL */
ret = file_write_integer(data->default_vl_file, -1); if (ret != 0) return;
/* What was the new value? */
ret = file_read_integer(data->default_vl_file, &new_default); if (ret != 0) return;
/* Did it take effect in a new process? */
child_vl = get_child_rdvl(data); if (child_vl != new_default) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
new_default, child_vl); return;
}
ksft_test_result_pass("%s maximum vector length %d\n", data->name,
new_default);
data->max_vl = new_default;
/* Can we read back a VL from prctl? */ staticvoid prctl_get(struct vec_data *data)
{ int ret;
ret = prctl(data->prctl_get); if (ret == -1) {
ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
data->name, errno, strerror(errno)); return;
}
/* Mask out any flags */
ret &= PR_SVE_VL_LEN_MASK;
/* Is that what we can read back directly? */ if (ret == data->rdvl())
ksft_test_result_pass("%s current VL is %d\n",
data->name, ret); else
ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",
data->name, ret, data->rdvl());
}
/* Does the prctl let us set the VL we already have? */ staticvoid prctl_set_same(struct vec_data *data)
{ int cur_vl = data->rdvl(); int ret;
ret = prctl(data->prctl_set, cur_vl); if (ret < 0) {
ksft_test_result_fail("%s prctl set failed: %d (%s)\n",
data->name, errno, strerror(errno)); return;
}
ksft_test_result(cur_vl == data->rdvl(), "%s set VL %d and have VL %d\n",
data->name, cur_vl, data->rdvl());
}
/* Can we set a new VL for this process? */ staticvoid prctl_set(struct vec_data *data)
{ int ret;
if (data->min_vl == data->max_vl) {
ksft_test_result_skip("%s only one VL supported\n",
data->name); return;
}
/* Try to set the minimum VL */
ret = prctl(data->prctl_set, data->min_vl); if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno)); return;
}
if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) {
ksft_test_result_fail("%s prctl set %d but return value is %d\n",
data->name, data->min_vl, data->rdvl()); return;
}
if (data->rdvl() != data->min_vl) {
ksft_test_result_fail("%s set %d but RDVL is %d\n",
data->name, data->min_vl, data->rdvl()); return;
}
/* Try to set the maximum VL */
ret = prctl(data->prctl_set, data->max_vl); if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->max_vl,
errno, strerror(errno)); return;
}
if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) {
ksft_test_result_fail("%s prctl() set %d but return value is %d\n",
data->name, data->max_vl, data->rdvl()); return;
}
/* The _INHERIT flag should not be present when we read the VL */
ret = prctl(data->prctl_get); if (ret == -1) {
ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
data->name, errno, strerror(errno)); return;
}
ksft_test_result_pass("%s prctl() set min/max\n", data->name);
}
/* If we didn't request it a new VL shouldn't affect the child */ staticvoid prctl_set_no_child(struct vec_data *data)
{ int ret, child_vl;
if (data->min_vl == data->max_vl) {
ksft_test_result_skip("%s only one VL supported\n",
data->name); return;
}
ret = prctl(data->prctl_set, data->min_vl); if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno)); return;
}
/* Ensure the default VL is different */
ret = file_write_integer(data->default_vl_file, data->max_vl); if (ret != 0) return;
/* Check that the child has the default we just set */
child_vl = get_child_rdvl(data); if (child_vl != data->max_vl) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
data->max_vl, child_vl); return;
}
ksft_test_result_pass("%s vector length used default\n", data->name);
/* If we didn't request it a new VL shouldn't affect the child */ staticvoid prctl_set_for_child(struct vec_data *data)
{ int ret, child_vl;
if (data->min_vl == data->max_vl) {
ksft_test_result_skip("%s only one VL supported\n",
data->name); return;
}
ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT); if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno)); return;
}
/* The _INHERIT flag should be present when we read the VL */
ret = prctl(data->prctl_get); if (ret == -1) {
ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
data->name, errno, strerror(errno)); return;
} if (!(ret & PR_SVE_VL_INHERIT)) {
ksft_test_result_fail("%s prctl() does not report _INHERIT\n",
data->name); return;
}
/* Ensure the default VL is different */
ret = file_write_integer(data->default_vl_file, data->max_vl); if (ret != 0) return;
/* Check that the child inherited our VL */
child_vl = get_child_rdvl(data); if (child_vl != data->min_vl) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
data->min_vl, child_vl); return;
}
ksft_test_result_pass("%s vector length was inherited\n", data->name);
/* _ONEXEC takes effect only in the child process */ staticvoid prctl_set_onexec(struct vec_data *data)
{ int ret, child_vl;
if (data->min_vl == data->max_vl) {
ksft_test_result_skip("%s only one VL supported\n",
data->name); return;
}
/* Set a known value for the default and our current VL */
ret = file_write_integer(data->default_vl_file, data->max_vl); if (ret != 0) return;
ret = prctl(data->prctl_set, data->max_vl); if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno)); return;
}
/* Set a different value for the child to have on exec */
ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC); if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno)); return;
}
/* Our current VL should stay the same */ if (data->rdvl() != data->max_vl) {
ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n",
data->name); return;
}
/* Check that the child inherited our VL */
child_vl = get_child_rdvl(data); if (child_vl != data->min_vl) {
ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n",
data->min_vl, child_vl); return;
}
ksft_test_result_pass("%s vector length set on exec\n", data->name);
/* For each VQ verify that setting via prctl() does the right thing */ staticvoid prctl_set_all_vqs(struct vec_data *data)
{ int ret, vq, vl, new_vl, i; int orig_vls[ARRAY_SIZE(vec_data)]; int errors = 0;
if (!data->min_vl || !data->max_vl) {
ksft_test_result_skip("%s Failed to enumerate VLs, not testing VL setting\n",
data->name); return;
}
for (i = 0; i < ARRAY_SIZE(vec_data); i++) { if (!vec_type_supported(&vec_data[i])) continue;
orig_vls[i] = vec_data[i].rdvl();
}
/* Attempt to set the VL */
ret = prctl(data->prctl_set, vl); if (ret < 0) {
errors++;
ksft_print_msg("%s prctl set failed for %d: %d (%s)\n",
data->name, vl,
errno, strerror(errno)); continue;
}
new_vl = ret & PR_SVE_VL_LEN_MASK;
/* Check that we actually have the reported new VL */ if (data->rdvl() != new_vl) {
ksft_print_msg("Set %s VL %d but RDVL reports %d\n",
data->name, new_vl, data->rdvl());
errors++;
}
/* Did any other VLs change? */ for (i = 0; i < ARRAY_SIZE(vec_data); i++) { if (&vec_data[i] == data) continue;
if (!vec_type_supported(&vec_data[i])) continue;
if (vec_data[i].rdvl() != orig_vls[i]) {
ksft_print_msg("%s VL changed from %d to %d\n",
vec_data[i].name, orig_vls[i],
vec_data[i].rdvl());
errors++;
}
}
/* Was that the VL we asked for? */ if (new_vl == vl) continue;
/* Should round up to the minimum VL if below it */ if (vl < data->min_vl) { if (new_vl != data->min_vl) {
ksft_print_msg("%s VL %d returned %d not minimum %d\n",
data->name, vl, new_vl,
data->min_vl);
errors++;
}
continue;
}
/* Should round down to maximum VL if above it */ if (vl > data->max_vl) { if (new_vl != data->max_vl) {
ksft_print_msg("%s VL %d returned %d not maximum %d\n",
data->name, vl, new_vl,
data->max_vl);
errors++;
}
continue;
}
/* Otherwise we should've rounded down */ if (!(new_vl < vl)) {
ksft_print_msg("%s VL %d returned %d, did not round down\n",
data->name, vl, new_vl);
errors++;
continue;
}
}
ksft_test_result(errors == 0, "%s prctl() set all VLs, %d errors\n",
data->name, errors);
}
typedefvoid (*test_type)(struct vec_data *);
staticconst test_type tests[] = { /* * The default/min/max tests must be first and in this order * to provide data for other tests.
*/
proc_read_default,
proc_write_min,
proc_write_max,
/* * Verify we can change the SVE vector length while SME is active and * continue to use SME afterwards.
*/ staticvoid change_sve_with_za(void)
{ struct vec_data *sve_data = &vec_data[VEC_SVE]; bool pass = true; int ret, i;
if (sve_data->min_vl == sve_data->max_vl) {
ksft_print_msg("Only one SVE VL supported, can't change\n");
ksft_test_result_skip("change_sve_while_sme\n"); return;
}
/* Ensure we will trigger a change when we set the maximum */
ret = prctl(sve_data->prctl_set, sve_data->min_vl); if (ret != sve_data->min_vl) {
ksft_print_msg("Failed to set SVE VL %d: %d\n",
sve_data->min_vl, ret);
pass = false;
}
/* Enable SM and ZA */
smstart();
/* Trigger another VL change */
ret = prctl(sve_data->prctl_set, sve_data->max_vl); if (ret != sve_data->max_vl) {
ksft_print_msg("Failed to set SVE VL %d: %d\n",
sve_data->max_vl, ret);
pass = false;
}
/* * Spin for a bit with SM enabled to try to trigger another * save/restore. We can't use syscalls without exiting * streaming mode.
*/ for (i = 0; i < 100000000; i++)
smstart_sm();
/* * TODO: Verify that ZA was preserved over the VL change and * spin.
*/
/* Clean up after ourselves */
smstop();
ret = prctl(sve_data->prctl_set, sve_data->default_vl); if (ret != sve_data->default_vl) {
ksft_print_msg("Failed to restore SVE VL %d: %d\n",
sve_data->default_vl, ret);
pass = false;
}
for (i = 0; i < ARRAY_SIZE(vec_data); i++) { struct vec_data *data = &vec_data[i]; unsignedlong supported;
supported = vec_type_supported(data); if (!supported)
all_supported = false;
for (j = 0; j < ARRAY_SIZE(tests); j++) { if (supported)
tests[j](data); else
ksft_test_result_skip("%s not supported\n",
data->name);
}
}
for (i = 0; i < ARRAY_SIZE(all_types_tests); i++) { if (all_supported)
all_types_tests[i].test(); else
ksft_test_result_skip("%s\n", all_types_tests[i].name);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.