info/L3/bit_usage annotates every cache portion as used by software ('X
or 'S') or not ('H' or '0'). Nothing in the suite checks it's reported
correctly.

Implement L3_BIT_USAGE to set the root group's CBM to a few masks and
confirm that, for every portion, bit_usage marks it software-used
exactly when it is in the CBM. With only the root group present this
must track the mask bit-for-bit.

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

diff --git a/tools/testing/selftests/resctrl/cat_test.c 
b/tools/testing/selftests/resctrl/cat_test.c
index d236988916d9..62af8ac98109 100644
--- a/tools/testing/selftests/resctrl/cat_test.c
+++ b/tools/testing/selftests/resctrl/cat_test.c
@@ -682,3 +682,156 @@ struct resctrl_test l3_cat_validate_test = {
        .feature_check = test_resource_feature_check,
        .run_test = cat_validate_run_test,
 };
+
+/*
+ * L3_BIT_USAGE - Verify info/L3/bit_usage reflects the allocation.
+ *
+ * bit_usage annotates each cache portion: 'X'/'S' mean a portion is used by
+ * software, 'H'/'0' mean it is not. With only the root group present, a
+ * portion is software-used exactly when it is in the root CBM, so bit_usage
+ * must track the CBM bit-for-bit.
+ */
+#define BIT_USAGE_LEN  256
+
+static bool bit_usage_sw_used(char c)
+{
+       return c == 'X' || c == 'S';
+}
+
+static bool bit_usage_not_used(char c)
+{
+       return c == 'H' || c == '0';
+}
+
+static int bit_usage_for_domain(const char *resource, int domain_id,
+                               char *out, size_t len)
+{
+       char path[1024], raw[BIT_USAGE_LEN], *tok, *save;
+       FILE *fp;
+
+       snprintf(path, sizeof(path), "%s/%s/bit_usage", INFO_PATH, resource);
+       fp = fopen(path, "r");
+       if (!fp) {
+               ksft_perror("Error opening bit_usage");
+               return -1;
+       }
+       if (!fgets(raw, sizeof(raw), fp)) {
+               ksft_perror("Error reading bit_usage");
+               fclose(fp);
+               return -1;
+       }
+       fclose(fp);
+
+       /* bit_usage is "id=chars;id=chars;..."; return the chars for 
domain_id. */
+       for (tok = strtok_r(raw, ";\n", &save); tok; tok = strtok_r(NULL, 
";\n", &save)) {
+               char *eq = strchr(tok, '=');
+
+               if (!eq)
+                       continue;
+               *eq = '\0';
+               if (atoi(tok) == domain_id) {
+                       snprintf(out, len, "%s", eq + 1);
+                       return 0;
+               }
+       }
+
+       ksft_print_msg("No bit_usage entry for domain %d\n", domain_id);
+       return -1;
+}
+
+static int bit_usage_check_mask(const struct resctrl_test *test, int cpu,
+                               int domain_id, unsigned long mask,
+                               int count_of_bits)
+{
+       char schemata[64], usage[BIT_USAGE_LEN];
+       int i, ret;
+
+       snprintf(schemata, sizeof(schemata), "%lx", mask);
+       ret = write_schemata("", schemata, cpu, test->resource);
+       if (ret) {
+               ksft_print_msg("Failed to set CBM 0x%lx\n", mask);
+               return ret;
+       }
+
+       ret = bit_usage_for_domain(test->resource, domain_id, usage, 
sizeof(usage));
+       if (ret)
+               return ret;
+
+       if (strlen(usage) != count_of_bits) {
+               ksft_print_msg("bit_usage \"%s\" has %zu chars, expected %d\n",
+                              usage, strlen(usage), count_of_bits);
+               return 1;
+       }
+
+       /* bit_usage prints the highest portion first, so usage[0] is bit N-1. 
*/
+       for (i = 0; i < count_of_bits; i++) {
+               int bit = count_of_bits - 1 - i;
+               bool in_cbm = (mask >> bit) & 1;
+               char c = usage[i];
+
+               if (!bit_usage_sw_used(c) && !bit_usage_not_used(c)) {
+                       ksft_print_msg("Unexpected bit_usage char '%c' for CBM 
0x%lx\n",
+                                      c, mask);
+                       return 1;
+               }
+               if (in_cbm != bit_usage_sw_used(c)) {
+                       ksft_print_msg("CBM 0x%lx portion %d shows '%c', %s 
allocation\n",
+                                      mask, bit, c, in_cbm ? "in" : "not in");
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int bit_usage_run_test(const struct resctrl_test *test,
+                             const struct user_params *uparams)
+{
+       unsigned long full_mask, masks[3];
+       char schemata[64];
+       int count_of_bits, domain_id, i, ret;
+
+       ret = get_full_cbm(test->resource, &full_mask);
+       if (ret)
+               return ret;
+
+       ret = get_domain_id(test->resource, uparams->cpu, &domain_id);
+       if (ret < 0)
+               return ret;
+
+       count_of_bits = count_bits(full_mask);
+
+       masks[0] = full_mask;                                   /* every 
portion */
+       masks[1] = create_bit_mask(0, count_of_bits / 2);       /* low half */
+       masks[2] = full_mask & ~masks[1];                       /* high half */
+
+       for (i = 0; i < 3; i++) {
+               ret = bit_usage_check_mask(test, uparams->cpu, domain_id,
+                                          masks[i], count_of_bits);
+               if (ret)
+                       break;
+       }
+
+       /* Restore the root group to the full CBM. */
+       snprintf(schemata, sizeof(schemata), "%lx", full_mask);
+       write_schemata("", schemata, uparams->cpu, test->resource);
+
+       if (!ret)
+               ksft_print_msg("Pass: bit_usage reflects the allocation\n");
+
+       return ret;
+}
+
+static bool bit_usage_feature_check(const struct resctrl_test *test)
+{
+       return test_resource_feature_check(test) &&
+              resource_info_file_exists(test->resource, "bit_usage");
+}
+
+struct resctrl_test l3_bit_usage_test = {
+       .name = "L3_BIT_USAGE",
+       .group = "CAT",
+       .resource = "L3",
+       .feature_check = bit_usage_feature_check,
+       .run_test = bit_usage_run_test,
+};
diff --git a/tools/testing/selftests/resctrl/resctrl.h 
b/tools/testing/selftests/resctrl/resctrl.h
index e2e3cf7833bc..270af2b26ba7 100644
--- a/tools/testing/selftests/resctrl/resctrl.h
+++ b/tools/testing/selftests/resctrl/resctrl.h
@@ -249,6 +249,7 @@ extern struct resctrl_test cmt_test;
 extern struct resctrl_test l3_cat_test;
 extern struct resctrl_test l3_cat_occup_test;
 extern struct resctrl_test l3_cat_validate_test;
+extern struct resctrl_test l3_bit_usage_test;
 extern struct resctrl_test l3_noncont_cat_test;
 extern struct resctrl_test l2_noncont_cat_test;
 
diff --git a/tools/testing/selftests/resctrl/resctrl_tests.c 
b/tools/testing/selftests/resctrl/resctrl_tests.c
index 66739e96f33c..e622928a2d7d 100644
--- a/tools/testing/selftests/resctrl/resctrl_tests.c
+++ b/tools/testing/selftests/resctrl/resctrl_tests.c
@@ -21,6 +21,7 @@ static struct resctrl_test *resctrl_tests[] = {
        &l3_cat_test,
        &l3_cat_occup_test,
        &l3_cat_validate_test,
+       &l3_bit_usage_test,
        &l3_noncont_cat_test,
        &l2_noncont_cat_test,
 };
-- 
2.43.0


Reply via email to