Implement MBA schemata rw test, in a temporary control group, write a few legal percentages to each MB domain, read each back through the schemata then restore the maximum and remove the group. Domain ids are taken from the schemata.
Add a resctrl_write_schemata() helper for the raw write. Signed-off-by: Richard Cheng <[email protected]> --- .../selftests/resctrl/mba_schemata_test.c | 115 ++++++++++++++++++ tools/testing/selftests/resctrl/resctrl.h | 2 + .../testing/selftests/resctrl/resctrl_tests.c | 1 + tools/testing/selftests/resctrl/resctrlfs.c | 36 ++++++ 4 files changed, 154 insertions(+) diff --git a/tools/testing/selftests/resctrl/mba_schemata_test.c b/tools/testing/selftests/resctrl/mba_schemata_test.c index b92011e42726..ff101b814982 100644 --- a/tools/testing/selftests/resctrl/mba_schemata_test.c +++ b/tools/testing/selftests/resctrl/mba_schemata_test.c @@ -150,3 +150,118 @@ struct resctrl_test mba_schemata_info_test = { .feature_check = mba_schemata_feature_check, .run_test = mba_schemata_info_run_test, }; + +/* Read back the percentage for one MB domain from a group's schemata. */ +static int mb_read_domain(const char *ctrlgrp, const char *resource, int dom, + unsigned int *out) +{ + unsigned int pct[MB_MAX_DOMAINS]; + int ids[MB_MAX_DOMAINS], n, i; + char vals[MB_SCHEMATA_LEN]; + + if (resctrl_get_schemata(ctrlgrp, resource, vals, sizeof(vals))) + return -1; + n = parse_mb_schemata(vals, ids, pct, MB_MAX_DOMAINS); + for (i = 0; i < n; i++) { + if (ids[i] == dom) { + *out = pct[i]; + return 0; + } + } + + return -1; +} + +/* + * writing a legal percentage to a domain takes effect + * and reads back, and restoring the maximum works. + * Domain ids come from the schemata directly. + */ +static int mba_schemata_rw_run_test(const struct resctrl_test *test, + const struct user_params *uparams) +{ + static const unsigned int cand[] = { 10, 50, 90 }; + unsigned int gran, pct[MB_MAX_DOMAINS], receive = 0; + int ids[MB_MAX_DOMAINS], n, i, j, ret, fail = 0; + char vals[MB_SCHEMATA_LEN], grp_path[256], line[64]; + const char *grp = "mba_schemata_rw"; + + if (resource_info_unsigned_get(test->resource, "bandwidth_gran", &gran)) + return 1; + + /* 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; + } + + 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 < n; i++) { + for (j = 0; j < (int)ARRAY_SIZE(cand); j++) { + snprintf(line, sizeof(line), "%s:%d=%u", + test->resource, ids[i], cand[j]); + ret = resctrl_write_schemata(grp, line); + if (ret) { + ksft_print_msg("Fail: write \"%s\" (ret=%d)\n", line, ret); + fail = 1; + continue; + } + if (mb_read_domain(grp, test->resource, ids[i], &receive)) { + ksft_print_msg("Fail: read back %s domain %d\n", + test->resource, ids[i]); + fail = 1; + continue; + } + if (abs((int)receive - (int)cand[j]) > (int)gran) { + ksft_print_msg("Fail: %s domain %d: %u%% -> %u%% (gran %u)\n", + test->resource, ids[i], cand[j], receive, gran); + fail = 1; + } else { + ksft_print_msg("Pass: %s domain %d %u%% -> read %u%%\n", + test->resource, ids[i], cand[j], receive); + } + } + + /* Restore the maximum and confirm. */ + snprintf(line, sizeof(line), "%s:%d=%u", + test->resource, ids[i], MB_PERCENT_MAX); + ret = resctrl_write_schemata(grp, line); + if (ret) { + ksft_print_msg("Fail: restore write \"%s\" (ret=%d)\n", line, ret); + fail = 1; + } else if (mb_read_domain(grp, test->resource, ids[i], &receive) || + receive != MB_PERCENT_MAX) { + ksft_print_msg("Fail: %s domain %d not restored to %u%% (received %u)\n", + test->resource, ids[i], MB_PERCENT_MAX, receive); + fail = 1; + } else { + ksft_print_msg("Pass: %s domain %d restored to %u%%\n", + test->resource, ids[i], MB_PERCENT_MAX); + } + } + + rmdir(grp_path); + + return fail; +} + +struct resctrl_test mba_schemata_rw_test = { + .name = "MBA_SCHEMATA_RW", + .group = "MBA_SCHEMATA", + .resource = "MB", + .feature_check = mba_schemata_feature_check, + .run_test = mba_schemata_rw_run_test, +}; diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h index a6ad25a08ae5..75d1ad5c6968 100644 --- a/tools/testing/selftests/resctrl/resctrl.h +++ b/tools/testing/selftests/resctrl/resctrl.h @@ -203,6 +203,7 @@ int get_cache_size(int cpu_no, const char *cache_type, unsigned long *cache_size int resource_info_unsigned_get(const char *resource, const char *filename, unsigned int *val); int resource_info_str_get(const char *resource, const char *filename, char *val, size_t len); int resctrl_get_schemata(const char *ctrlgrp, const char *resource, char *buf, size_t len); +int resctrl_write_schemata(const char *ctrlgrp, const char *line); void ctrlc_handler(int signum, siginfo_t *info, void *ptr); int signal_handler_register(const struct resctrl_test *test); void signal_handler_unregister(void); @@ -249,5 +250,6 @@ extern struct resctrl_test l3_cat_test; 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; #endif /* RESCTRL_H */ diff --git a/tools/testing/selftests/resctrl/resctrl_tests.c b/tools/testing/selftests/resctrl/resctrl_tests.c index 5d45ac95d988..9b3ac0437bf9 100644 --- a/tools/testing/selftests/resctrl/resctrl_tests.c +++ b/tools/testing/selftests/resctrl/resctrl_tests.c @@ -22,6 +22,7 @@ static struct resctrl_test *resctrl_tests[] = { &l3_noncont_cat_test, &l2_noncont_cat_test, &mba_schemata_info_test, + &mba_schemata_rw_test, }; static unsigned int detect_vendor(void) diff --git a/tools/testing/selftests/resctrl/resctrlfs.c b/tools/testing/selftests/resctrl/resctrlfs.c index 8b428a22496d..1196418461b5 100644 --- a/tools/testing/selftests/resctrl/resctrlfs.c +++ b/tools/testing/selftests/resctrl/resctrlfs.c @@ -521,6 +521,42 @@ int resctrl_get_schemata(const char *ctrlgrp, const char *resource, return ret; } +/* + * resctrl_write_schemata - Write a raw schemata line to a group's schemata file + * @ctrlgrp: Control group name, or "" / NULL for the default group + * @line: Full schemata line without newline, e.g. "MB:0=50" + * + * Unlike write_schemata(), the caller supplies the exact + * "<resource>:<domain>=<value>", so this works where the domain id is not the + * CPU's L3 cache id (e.g. ARM MPAM, whose MB domains are NUMA proximity ids). + * + * Return: 0 on success, or -errno on failure (e.g. -EINVAL for a value the + * kernel rejects). + */ +int resctrl_write_schemata(const char *ctrlgrp, const char *line) +{ + char path[1024], buf[1024]; + int fd, len, ret = 0; + + if (ctrlgrp && *ctrlgrp) + snprintf(path, sizeof(path), "%s/%s/schemata", RESCTRL_PATH, ctrlgrp); + else + snprintf(path, sizeof(path), "%s/schemata", RESCTRL_PATH); + + len = snprintf(buf, sizeof(buf), "%s\n", line); + if (len < 0 || (size_t)len >= sizeof(buf)) + return -EOVERFLOW; + + fd = open(path, O_WRONLY); + if (fd < 0) + return -errno; + if (write(fd, buf, len) < 0) + ret = -errno; + close(fd); + + return ret; +} + /* * create_bit_mask- Create bit mask from start, len pair * @start: LSB of the mask -- 2.43.0

