The resctrl ABI requires invalid MB schemata writes to fail and report a diagnostic in info/last_cmd_status.
Add MBA_SCHEMATA_INVAL. In a temporary control group it writes an out-of-range value 101, malformed values abc, -5, and a non-existent domain id, asserting each write fails and last_cmd_status contains the expected substring. The bad-domain case checks errno only, as the kernel does not update last_cmd_status there. A final write of the maximum percentage must succeed and report "ok". Signed-off-by: Richard Cheng <[email protected]> --- .../selftests/resctrl/mba_schemata_test.c | 123 ++++++++++++++++++ tools/testing/selftests/resctrl/resctrl.h | 1 + .../testing/selftests/resctrl/resctrl_tests.c | 1 + 3 files changed, 125 insertions(+) diff --git a/tools/testing/selftests/resctrl/mba_schemata_test.c b/tools/testing/selftests/resctrl/mba_schemata_test.c index ff101b814982..0cd257dcec91 100644 --- a/tools/testing/selftests/resctrl/mba_schemata_test.c +++ b/tools/testing/selftests/resctrl/mba_schemata_test.c @@ -265,3 +265,126 @@ struct resctrl_test mba_schemata_rw_test = { .feature_check = mba_schemata_feature_check, .run_test = mba_schemata_rw_run_test, }; + +/* + * Read the first line of info/last_cmd_status. + */ +static int read_last_cmd_status(char *status, size_t len) +{ + return resource_info_str_get("", "last_cmd_status", status, len); +} + +/* + * invalid schemata writes (out-of-range value, malformed value, nonexistent + * domain) must be rejected with a diagnostic in info/last_cmd_status, and a + * subsequent valid write must still succeed and report "ok". + */ +static int mba_schemata_inval_run_test(const struct resctrl_test *test, + const struct user_params *uparams) +{ + /* Rejected values and the stable last_cmd_status fragment expected. */ + static const struct { + const char *val; + const char *substr; + } bad[] = { + { "101", "out of range" }, + { "abc", "Invalid MB value" }, + { "-5", "Invalid MB value" }, + }; + unsigned int pct[MB_MAX_DOMAINS]; + int ids[MB_MAX_DOMAINS], n, i, ret, bad_dom, fail = 0; + char vals[MB_SCHEMATA_LEN], grp_path[256], line[64], status[256]; + const char *grp = "mba_schemata_inval"; + + /* Enumerate the real MB domain ids from the default schemata. */ + ret = resctrl_get_schemata("", test->resource, vals, sizeof(vals)); + if (ret) { + ksft_print_msg("Fail: could not read %s schemata line (ret=%d)\n", + test->resource, ret); + return 1; + } + n = parse_mb_schemata(vals, ids, pct, MB_MAX_DOMAINS); + if (n < 1) { + ksft_print_msg("Fail: could not parse %s schemata \"%s\"\n", + test->resource, vals); + return 1; + } + + /* A domain id one past the largest real one cannot exist. */ + bad_dom = ids[0]; + for (i = 1; i < n; i++) + if (ids[i] > bad_dom) + bad_dom = ids[i]; + bad_dom++; + + snprintf(grp_path, sizeof(grp_path), "%s/%s", RESCTRL_PATH, grp); + if (mkdir(grp_path, 0755) && errno != EEXIST) { + ksft_print_msg("Fail: mkdir %s: %m\n", grp_path); + return 1; + } + + for (i = 0; i < (int)ARRAY_SIZE(bad); i++) { + snprintf(line, sizeof(line), "%s:%d=%s", + test->resource, ids[0], bad[i].val); + ret = resctrl_write_schemata(grp, line); + if (!ret) { + ksft_print_msg("Fail: invalid write \"%s\" was accepted\n", + line); + fail = 1; + continue; + } + if (read_last_cmd_status(status, sizeof(status))) { + fail = 1; + continue; + } + if (!strstr(status, bad[i].substr)) { + ksft_print_msg("Fail: \"%s\": last_cmd_status \"%s\" lacks \"%s\"\n", + line, status, bad[i].substr); + fail = 1; + } + } + + /* + * A write to a nonexistent domain also fails, but the kernel does not + * record it in last_cmd_status, so assert on the write error only. + * Use the maximum percentage so the rejection can only be due to the + * domain id. + */ + snprintf(line, sizeof(line), "%s:%d=%u", + test->resource, bad_dom, MB_PERCENT_MAX); + ret = resctrl_write_schemata(grp, line); + if (!ret) { + ksft_print_msg("Fail: write \"%s\" to nonexistent domain was accepted\n", + line); + fail = 1; + } + + /* + * The rejected writes must not break a following valid write. + */ + snprintf(line, sizeof(line), "%s:%d=%u", + test->resource, ids[0], MB_PERCENT_MAX); + ret = resctrl_write_schemata(grp, line); + if (ret) { + ksft_print_msg("Fail: valid write \"%s\" (ret=%d)\n", line, ret); + fail = 1; + } else if (read_last_cmd_status(status, sizeof(status))) { + fail = 1; + } else if (strcmp(status, "ok")) { + ksft_print_msg("Fail: valid write \"%s\": last_cmd_status \"%s\" != \"ok\"\n", + line, status); + fail = 1; + } + + rmdir(grp_path); + + return fail; +} + +struct resctrl_test mba_schemata_inval_test = { + .name = "MBA_SCHEMATA_INVAL", + .group = "MBA_SCHEMATA", + .resource = "MB", + .feature_check = mba_schemata_feature_check, + .run_test = mba_schemata_inval_run_test, +}; diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h index 75d1ad5c6968..c32ab5c2cc56 100644 --- a/tools/testing/selftests/resctrl/resctrl.h +++ b/tools/testing/selftests/resctrl/resctrl.h @@ -251,5 +251,6 @@ extern struct resctrl_test l3_noncont_cat_test; extern struct resctrl_test l2_noncont_cat_test; extern struct resctrl_test mba_schemata_info_test; extern struct resctrl_test mba_schemata_rw_test; +extern struct resctrl_test mba_schemata_inval_test; #endif /* RESCTRL_H */ diff --git a/tools/testing/selftests/resctrl/resctrl_tests.c b/tools/testing/selftests/resctrl/resctrl_tests.c index 9b3ac0437bf9..7a7184783e6a 100644 --- a/tools/testing/selftests/resctrl/resctrl_tests.c +++ b/tools/testing/selftests/resctrl/resctrl_tests.c @@ -23,6 +23,7 @@ static struct resctrl_test *resctrl_tests[] = { &l2_noncont_cat_test, &mba_schemata_info_test, &mba_schemata_rw_test, + &mba_schemata_inval_test, }; static unsigned int detect_vendor(void) -- 2.43.0

