From: Richard Cheng <icheng@nvidia.com>
To: tony.luck@intel.co, shuah@kernel.org, reinette.chatre@intel.com
Cc: Dave.Martin@arm.com, james.morse@arm.com, babu.moger@amd.com,
linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
fenghuay@nvidia.com, newtonl@nvidia.com, kristinc@nvidia.com,
kaihengf@nvidia.com, kobak@nvidia.com, sdonthineni@nvidia.com,
Richard Cheng <icheng@nvidia.com>
Subject: [PATCH v2 2/2] selftests/resctrl: Add L3_BIT_USAGE to check allocation reporting
Date: Mon, 10 Aug 2026 17:51:23 +0800 [thread overview]
Message-ID: <20260810095124.32244-3-icheng@nvidia.com> (raw)
In-Reply-To: <20260810095124.32244-1-icheng@nvidia.com>
Add L3_BIT_USAGE to verify that info/L3/bit_usage follows the root
group's CBM.
Write full, low, and high valid masks, then check the selected domain.
Require X or S for portions used by SW and H or 0 for portions outside
the allocation.
Signed-off-by: Richard Cheng <icheng@nvidia.com>
---
Changelog:
v1 -> v2:
- Simplify helper names.
- Add an explicit character-validity helper
- Simplify parsing and avoid fixed-length input
- Move domain lookup into the mask checker
- Generate masks according to min_cbm_bits
- Remove unnecessary CBM restoration
- Move tail comments above assignments
- Use KSFT_FAIL and reverse-fir-tree ordering
---
tools/testing/selftests/resctrl/cat_test.c | 194 ++++++++++++++++++
tools/testing/selftests/resctrl/resctrl.h | 1 +
.../testing/selftests/resctrl/resctrl_tests.c | 1 +
3 files changed, 196 insertions(+)
diff --git a/tools/testing/selftests/resctrl/cat_test.c b/tools/testing/selftests/resctrl/cat_test.c
index a06c57954740..9205f93fd669 100644
--- a/tools/testing/selftests/resctrl/cat_test.c
+++ b/tools/testing/selftests/resctrl/cat_test.c
@@ -440,6 +440,192 @@ static int cbm_validate_run_test(const struct resctrl_test *test,
return 0;
}
+/*
+ * L3_BIT_USAGE - Verify info/L3/bit_usage reflects the allocation.
+ *
+ * bit_usage annotates each cache portion. 'X' and 'S' mean software uses the
+ * portion, while 'H' and '0' mean it does not. With only the root group
+ * present, bit_usage must track the root CBM bit-for-bit.
+ */
+static bool bit_used_by_sw(char c)
+{
+ return c == 'X' || c == 'S';
+}
+
+static bool bit_unused_by_sw(char c)
+{
+ return c == 'H' || c == '0';
+}
+
+static bool bit_usage_valid(char c)
+{
+ return bit_used_by_sw(c) || bit_unused_by_sw(c);
+}
+
+static int bit_usage_for_domain(const char *resource, int domain_id,
+ char *out, size_t out_len)
+{
+ char path[PATH_MAX], *line = NULL, *saveptr;
+ char *usage, *endptr, *id;
+ size_t line_len = 0;
+ long parsed_id;
+ ssize_t bytes;
+ FILE *fp;
+ int ret;
+
+ snprintf(path, sizeof(path), "%s/%s/bit_usage", INFO_PATH, resource);
+ fp = fopen(path, "r");
+ if (!fp) {
+ ksft_perror("Error opening bit_usage");
+ return -errno;
+ }
+
+ bytes = getline(&line, &line_len, fp);
+ fclose(fp);
+ if (bytes < 0) {
+ ksft_print_msg("Error reading bit_usage\n");
+ free(line);
+ return -EIO;
+ }
+
+ ret = -ENOENT;
+ for (id = strtok_r(line, "=;\n", &saveptr); id;
+ id = strtok_r(NULL, "=;\n", &saveptr)) {
+ usage = strtok_r(NULL, "=;\n", &saveptr);
+ if (!usage)
+ break;
+
+ errno = 0;
+ parsed_id = strtol(id, &endptr, 10);
+ if (errno || *endptr || parsed_id != domain_id)
+ continue;
+
+ ret = snprintf(out, out_len, "%s", usage);
+ if (ret < 0 || (size_t)ret >= out_len)
+ ret = -ENOSPC;
+ else
+ ret = 0;
+ goto out;
+ }
+
+ ksft_print_msg("No bit_usage entry for domain %d\n", domain_id);
+out:
+ free(line);
+ return ret;
+}
+
+static int bit_usage_check_mask(const struct resctrl_test *test, int cpu,
+ unsigned long mask, unsigned int count_of_bits)
+{
+ char usage[sizeof(unsigned long) * 8 + 1];
+ char schemata[64];
+ unsigned int i;
+ int domain_id;
+ int 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 = get_domain_id(test->resource, cpu, &domain_id);
+ if (ret < 0)
+ 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 %u\n",
+ usage, strlen(usage), count_of_bits);
+ return KSFT_FAIL;
+ }
+
+ for (i = 0; i < count_of_bits; i++) {
+ bool in_cbm;
+ int bit;
+ char c;
+
+ bit = count_of_bits - 1 - i;
+ in_cbm = (mask >> bit) & 1;
+ c = usage[i];
+
+ if (!bit_usage_valid(c)) {
+ ksft_print_msg("Invalid bit_usage character '%c' for CBM 0x%lx\n",
+ c, mask);
+ return KSFT_FAIL;
+ }
+ if (in_cbm != bit_used_by_sw(c)) {
+ ksft_print_msg("CBM 0x%lx portion %d shows '%c', %s allocation\n",
+ mask, bit, c, in_cbm ? "in" : "not in");
+ return KSFT_FAIL;
+ }
+ }
+
+ return 0;
+}
+
+static int bit_usage_run_test(const struct resctrl_test *test,
+ const struct user_params *uparams)
+{
+ unsigned int count_of_bits, min_cbm_bits, partial_bits;
+ unsigned long full_mask, high_mask, masks[3];
+ unsigned int nr_masks = 1;
+ unsigned int i;
+ int ret;
+
+ ret = get_full_cbm(test->resource, &full_mask);
+ if (ret)
+ return ret;
+
+ ret = resource_info_unsigned_get(test->resource, "min_cbm_bits",
+ &min_cbm_bits);
+ if (ret)
+ return ret;
+
+ count_of_bits = count_bits(full_mask);
+
+ /* Every cache portion. */
+ masks[0] = full_mask;
+
+ if (count_of_bits > min_cbm_bits) {
+ partial_bits = count_of_bits / 2;
+ partial_bits = max(partial_bits, min_cbm_bits);
+
+ /* Lowest valid partial allocation. */
+ masks[nr_masks++] = create_bit_mask(0, partial_bits);
+
+ if (partial_bits) {
+ /* Highest valid partial allocation. */
+ high_mask = create_bit_mask(count_of_bits - partial_bits,
+ partial_bits);
+ if (high_mask != masks[nr_masks - 1])
+ masks[nr_masks++] = high_mask;
+ }
+ }
+
+ for (i = 0; i < nr_masks; i++) {
+ ret = bit_usage_check_mask(test, uparams->cpu, masks[i],
+ count_of_bits);
+ if (ret)
+ return ret;
+ }
+
+ ksft_print_msg("Pass: bit_usage reflects the allocation\n");
+
+ return 0;
+}
+
+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_cat_test = {
.name = "L3_CAT",
.group = "CAT",
@@ -457,6 +643,14 @@ struct resctrl_test l3_cbm_validate_test = {
.run_test = cbm_validate_run_test,
};
+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,
+};
+
struct resctrl_test l3_noncont_cat_test = {
.name = "L3_NONCONT_CAT",
.group = "CAT",
diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h
index a6fc688ed997..765aa5d5814d 100644
--- a/tools/testing/selftests/resctrl/resctrl.h
+++ b/tools/testing/selftests/resctrl/resctrl.h
@@ -241,6 +241,7 @@ extern struct resctrl_test mba_test;
extern struct resctrl_test cmt_test;
extern struct resctrl_test l3_cat_test;
extern struct resctrl_test l3_cbm_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 57a4d0815b67..e6e948d55f38 100644
--- a/tools/testing/selftests/resctrl/resctrl_tests.c
+++ b/tools/testing/selftests/resctrl/resctrl_tests.c
@@ -20,6 +20,7 @@ static struct resctrl_test *resctrl_tests[] = {
&cmt_test,
&l3_cat_test,
&l3_cbm_validate_test,
+ &l3_bit_usage_test,
&l3_noncont_cat_test,
&l2_noncont_cat_test,
};
--
2.53.0
prev parent reply other threads:[~2026-08-10 9:51 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 9:51 [PATCH v2 0/2] selftests/resctrl: Increase L3 cache test coverage Richard Cheng
2026-08-10 9:51 ` [PATCH v2 1/2] selftests/resctrl: Add L3_CBM_VALIDATE to check CBM writes Richard Cheng
2026-08-10 9:51 ` Richard Cheng [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260810095124.32244-3-icheng@nvidia.com \
--to=icheng@nvidia.com \
--cc=Dave.Martin@arm.com \
--cc=babu.moger@amd.com \
--cc=fenghuay@nvidia.com \
--cc=james.morse@arm.com \
--cc=kaihengf@nvidia.com \
--cc=kobak@nvidia.com \
--cc=kristinc@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=newtonl@nvidia.com \
--cc=reinette.chatre@intel.com \
--cc=sdonthineni@nvidia.com \
--cc=shuah@kernel.org \
--cc=tony.luck@intel.co \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.