Hi Richard,

Thank you very much for creating this test.

On 6/8/26 4:06 AM, Richard Cheng wrote:
> 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)

There is a bit of redundancy here with the multiple appearances of "use".
How about "bit_sw_used()" or "bit_used_by_sw()" or ...?

> +{
> +     return c == 'X' || c == 'S';
> +}
> +
> +static bool bit_usage_not_used(char c)

(same comment about multiple "use" appearances)

> +{
> +     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';

Above looks like a re-implementation of strtok?

> +             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)) {

Could this be simplified (by avoiding double negative) to 
                if (bit_usage_sw_used(c) && bit_usage_not_used(c)) ?

> +                     ksft_print_msg("Unexpected bit_usage char '%c' for CBM 
> 0x%lx\n",

I think "Conflicting" instead of  "Unexpected" would be more specific about what
the issue is.

> +                                    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;

(reverse fir)

> +
> +     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;

Since this function does not use domain ID, could this be simplified by letting
bit_usage_check_mask() determine the domain ID itself?

> +
> +     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 */

Please do not use tail comments. Instead move comments above the line they
apply to.

> +
> +     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);

It is not necessary to restore the state - resctrl fs is unmounted after every 
test.

> +
> +     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,
>  };

Reinette

Reply via email to