Re: [PATCH 3/3] selftests/resctrl: Add L3_BIT_USAGE to check bit_usage tracks allocation

Reinette Chatre <[email protected]>
Newsgroups gmane.linux.kernel
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.