MB cpas are per control group, implement MBA_SCHEMATA_ISOLATE test to
verify a write to one group leaves other groups and the default group
untouched.

Create 2 groups, set the first to the max and the second to a lower cap,
then read the first after writing the second, each holds its own value.
The default group stays unchanged. The lower cap is gran-aligned and at
or above min_bandwidth, so read-back is exact and provably below the
maximum.

Signed-off-by: Richard Cheng <[email protected]>
---
 .../selftests/resctrl/mba_schemata_test.c     | 131 ++++++++++++++++++
 tools/testing/selftests/resctrl/resctrl.h     |   1 +
 .../testing/selftests/resctrl/resctrl_tests.c |   1 +
 3 files changed, 133 insertions(+)

diff --git a/tools/testing/selftests/resctrl/mba_schemata_test.c 
b/tools/testing/selftests/resctrl/mba_schemata_test.c
index 0cd257dcec91..d98946b831b1 100644
--- a/tools/testing/selftests/resctrl/mba_schemata_test.c
+++ b/tools/testing/selftests/resctrl/mba_schemata_test.c
@@ -388,3 +388,134 @@ struct resctrl_test mba_schemata_inval_test = {
        .feature_check = mba_schemata_feature_check,
        .run_test = mba_schemata_inval_run_test,
 };
+
+/*
+ * MB allocations are per control group: two groups hold different
+ * percentages on the same domain without disturbing each other or the
+ * default group.
+ */
+static int mba_schemata_isolate_run_test(const struct resctrl_test *test,
+                                        const struct user_params *uparams)
+{
+       unsigned int gran, min_bw, low, num_closids, def_before = 0, def_after 
= 0;
+       unsigned int got1 = 0, got2 = 0, pct[MB_MAX_DOMAINS];
+       int ids[MB_MAX_DOMAINS], n, ret, dom, fail = 0;
+       char vals[MB_SCHEMATA_LEN], path1[256], path2[256], line[64];
+       const char *grp1 = "mba_schemata_iso1", *grp2 = "mba_schemata_iso2";
+
+       /*
+        * Isolation works with any CLOSID count, but the
+        * default group plus the two test groups each need to hold one.
+        */
+       if (resource_info_unsigned_get(test->resource, "num_closids", 
&num_closids))
+               return 1;
+       if (num_closids < 3) {
+               ksft_print_msg("Skipping: num_closids=%u < 3, cannot create two 
test groups\n",
+                              num_closids);
+               return 0;
+       }
+
+       if (resource_info_unsigned_get(test->resource, "bandwidth_gran", &gran) 
|| !gran)
+               return 1;
+       if (resource_info_unsigned_get(test->resource, "min_bandwidth", 
&min_bw))
+               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;
+       }
+       dom = ids[0];
+
+       /*
+        * grp2's cap must stay provably below grp1's 100% even after the
+        * kernel rounds it to bandwidth_gran: align it here, at or above
+        * min_bandwidth, and expect an exact read-back. If no aligned value
+        * below the maximum exists, the two groups cannot be told apart.
+        */
+       low = min_bw > 20 ? min_bw : 20;
+       low = (low + gran - 1) / gran * gran;
+       if (low >= MB_PERCENT_MAX) {
+               ksft_print_msg("Skipping: no %s cap below %u representable 
(min_bw=%u, gran=%u)\n",
+                              test->resource, MB_PERCENT_MAX, min_bw, gran);
+               return 0;
+       }
+
+       if (mb_read_domain("", test->resource, dom, &def_before)) {
+               ksft_print_msg("Fail: read default group %s domain %d\n",
+                              test->resource, dom);
+               return 1;
+       }
+
+       snprintf(path1, sizeof(path1), "%s/%s", RESCTRL_PATH, grp1);
+       if (mkdir(path1, 0755) && errno != EEXIST) {
+               ksft_print_msg("Fail: mkdir %s: %m\n", path1);
+               return 1;
+       }
+       snprintf(path2, sizeof(path2), "%s/%s", RESCTRL_PATH, grp2);
+       if (mkdir(path2, 0755) && errno != EEXIST) {
+               ksft_print_msg("Fail: mkdir %s: %m\n", path2);
+               rmdir(path1);
+               return 1;
+       }
+
+       /* Pin the two groups to different caps on the same domain. */
+       snprintf(line, sizeof(line), "%s:%d=%u",
+                test->resource, dom, MB_PERCENT_MAX);
+       ret = resctrl_write_schemata(grp1, line);
+       if (ret) {
+               ksft_print_msg("Fail: write \"%s\" to %s (ret=%d)\n",
+                              line, grp1, ret);
+               fail = 1;
+       }
+       snprintf(line, sizeof(line), "%s:%d=%u", test->resource, dom, low);
+       ret = resctrl_write_schemata(grp2, line);
+       if (ret) {
+               ksft_print_msg("Fail: write \"%s\" to %s (ret=%d)\n",
+                              line, grp2, ret);
+               fail = 1;
+       }
+
+       /* Each group keeps its own cap; reading g1 after writing g2. */
+       if (mb_read_domain(grp1, test->resource, dom, &got1) ||
+           got1 != MB_PERCENT_MAX) {
+               ksft_print_msg("Fail: %s domain %d: %u%%, expected %u%%\n",
+                              grp1, dom, got1, MB_PERCENT_MAX);
+               fail = 1;
+       }
+       if (mb_read_domain(grp2, test->resource, dom, &got2) ||
+           got2 != low) {
+               ksft_print_msg("Fail: %s domain %d: %u%%, expected %u%%\n",
+                              grp2, dom, got2, low);
+               fail = 1;
+       }
+
+       /* The default group must be untouched by either write. */
+       if (mb_read_domain("", test->resource, dom, &def_after) ||
+           def_after != def_before) {
+               ksft_print_msg("Fail: default group domain %d changed %u%% -> 
%u%%\n",
+                              dom, def_before, def_after);
+               fail = 1;
+       }
+
+       rmdir(path1);
+       rmdir(path2);
+
+       return fail;
+}
+
+struct resctrl_test mba_schemata_isolate_test = {
+       .name = "MBA_SCHEMATA_ISOLATE",
+       .group = "MBA_SCHEMATA",
+       .resource = "MB",
+       .feature_check = mba_schemata_feature_check,
+       .run_test = mba_schemata_isolate_run_test,
+};
diff --git a/tools/testing/selftests/resctrl/resctrl.h 
b/tools/testing/selftests/resctrl/resctrl.h
index c32ab5c2cc56..07a67ac9292c 100644
--- a/tools/testing/selftests/resctrl/resctrl.h
+++ b/tools/testing/selftests/resctrl/resctrl.h
@@ -252,5 +252,6 @@ 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;
+extern struct resctrl_test mba_schemata_isolate_test;
 
 #endif /* RESCTRL_H */
diff --git a/tools/testing/selftests/resctrl/resctrl_tests.c 
b/tools/testing/selftests/resctrl/resctrl_tests.c
index 7a7184783e6a..583ee97bac20 100644
--- a/tools/testing/selftests/resctrl/resctrl_tests.c
+++ b/tools/testing/selftests/resctrl/resctrl_tests.c
@@ -24,6 +24,7 @@ static struct resctrl_test *resctrl_tests[] = {
        &mba_schemata_info_test,
        &mba_schemata_rw_test,
        &mba_schemata_inval_test,
+       &mba_schemata_isolate_test,
 };
 
 static unsigned int detect_vendor(void)
-- 
2.43.0


Reply via email to