BPF List
 help / color / mirror / Atom feed
From: Tao Cui <cui.tao@linux.dev>
To: sashiko-reviews@lists.linux.dev
Cc: cui.tao@linux.dev, bpf@vger.kernel.org, tj@kernel.org,
	hannes@cmpxchg.org, mkoutny@suse.com
Subject: Re: [RFC PATCH v12 3/4] selftests/bpf: add iocost cost model test
Date: Wed, 4 Nov 2026 11:15:35 +0800	[thread overview]
Message-ID: <fa7b359c-12a0-4801-97ee-5c11052c6a30@linux.dev> (raw)
In-Reply-To: <sashiko-outbox-195431@kernel.org>

Hello,

在 2026/11/3 22:16, sashiko-bot@kernel.org 写道:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [Medium] selftests/bpf: missing leading slash in cgroup paths in iocost_model tests
Confirmed, and the test host did accumulate leftovers named
cgroup-test-work-dir<pid>iocost_pairing. Will pass "/iocost_pairing"
and "/iocost_ms" so the cgroups are created inside the per-pid work
directory and nftw() in __cleanup_cgroup_environment() removes them.
> - [Medium] selftests/bpf: iocost_bpf_supported() requires /proc/config.gz to run tests
You're right. This was already pointed out in the v11 review.
I removed the unused `any` variable in v12 without fixing the
underlying issue.

I'll make the probe distinguish an unavailable kernel config
from CONFIG_BLK_CGROUP_IOCOST_BPF=n instead of treating both as
unsupported.

> - [Low] selftests/bpf: unpaired cleanup_cgroup_environment() in iocost_model
Right — in serial_test_iocost_model_streams() the environment is set
up after attach, so the skel_load and attach failure paths call
cleanup before anything was created. Will drop cleanup from those two
paths and keep it only where the setup has run.
> - [Low] selftests/bpf: missing rmdir() on error paths in nullb_create()
Confirmed. If either the size or power write fails, nullb_create()
returns without removing the instance directory, leaving the
configfs instance behind.

I'll mirror the read_file_token() failure path: power the instance
off and remove the directory before returning.

Thanks,
Tao
> --
> 
> commit 853e704fa28b8b44bb71705b9e1a9ac55510a4dd
> Author: Tao Cui <cuitao@kylinos.cn>
> 
> selftests/bpf: add iocost cost model test
> 
> This commit adds two BPF iocost cost models (a doubled linear HDD model and a
> multi-stream sequentiality model) along with selftests that attach them to a
> configfs-created null_blk device and verify pricing, tracepoints, model
> switching, and device removal.
> 
>> diff --git a/tools/testing/selftests/bpf/prog_tests/iocost_model.c b/tools/testing/selftests/bpf/prog_tests/iocost_model.c
>> new file mode 100644
>> index 0000000000000..fa9280fe9e34b
>> --- /dev/null
>> +++ b/tools/testing/selftests/bpf/prog_tests/iocost_model.c
>> @@ -0,0 +1,748 @@
> 
> [ ... ]
> 
>> +static int nullb_create(const char *name, char *dev, size_t dev_sz)
>> +{
>> +	char path[128];
>> +	char p2[128];
>> +	int err;
>> +
>> +	snprintf(path, sizeof(path), "/sys/kernel/config/nullb/%s", name);
>> +	if (mkdir(path, 0755)) {
>> +		if (errno != EEXIST)
>> +			return -errno;
>> +		/*
>> +		 * a leftover instance from an earlier crashed run may
>> +		 * still be powered: tear it down before reusing the
>> +		 * name
>> +		 */
>> +		snprintf(p2, sizeof(p2),
>> +			 "/sys/kernel/config/nullb/%s/power", name);
>> +		configfs_write(p2, "0");
>> +		if (rmdir(path) && errno != ENOENT)
>> +			return -errno;
>> +		if (mkdir(path, 0755) && errno != EEXIST)
>> +			return -errno;
>> +	}
>> +
>> +	snprintf(path, sizeof(path), "/sys/kernel/config/nullb/%s/size", name);
>> +	err = configfs_write(path, "128");
>> +	if (err)
>> +		return err;
>> +
>> +	snprintf(path, sizeof(path), "/sys/kernel/config/nullb/%s/power", name);
>> +	err = configfs_write(path, "1");
>> +	if (err)
>> +		return err;
> 
> [Severity: Low]
> As reported by Sashiko on RFC v11 [1], if configfs_write() fails when writing
> to size or power in nullb_create(), does this return early and leave the
> /sys/kernel/config/nullb/<name> directory and its underlying kernel
> nullb_device allocated?
> 
> In serial_test_iocost_model() and serial_test_iocost_model_streams(), the
> callers also return directly on nullb_create() failure without calling
> nullb_remove(IOCOST_NULLB). Should nullb_create() remove the instance
> directory on these error paths just like the read_file_token() failure path
> below?
> 
>> +
>> +	snprintf(path, sizeof(path), "/sys/block/%s/dev", name);
>> +	err = read_file_token(path, dev, dev_sz);
>> +	if (err) {
>> +		/* do not leave a powered instance behind */
>> +		snprintf(path, sizeof(path),
>> +			 "/sys/kernel/config/nullb/%s/power", name);
>> +		configfs_write(path, "0");
>> +		snprintf(path, sizeof(path),
>> +			 "/sys/kernel/config/nullb/%s", name);
>> +		rmdir(path);
>> +	}
>> +	return err;
>> +}
> 
> [ ... ]
> 
>> +static bool iocost_bpf_supported(void)
>> +{
>> +	FILE *f;
>> +	char line[128];
>> +	bool on = false;
>> +
>> +	f = popen("zcat /proc/config.gz 2>/dev/null", "r");
>> +	if (!f)
>> +		return false;
>> +	while (fgets(line, sizeof(line), f)) {
>> +		if (strstr(line, "CONFIG_BLK_CGROUP_IOCOST_BPF=y"))
>> +			on = true;
>> +	}
>> +	pclose(f);
>> +	return on;
>> +}
> 
> [Severity: Medium]
> As reported by Tejun Heo on RFC v11 [2], won't iocost_bpf_supported() still
> return false and cause iocost_test_env() to skip all three tests whenever
> /proc/config.gz is not present on the system, even if the running kernel has
> CONFIG_BLK_CGROUP_IOCOST_BPF=y enabled?
> 
> When CONFIG_IKCONFIG_PROC is disabled, popen() still succeeds while zcat
> outputs nothing, so fgets() reads zero lines and on remains false. In RFC v11
> it was noted that the any variable tracking whether any lines were read was
> set but never used; here any was removed rather than used to avoid skipping
> when /proc/config.gz is unavailable (or falling back to /boot/config-<release>
> or BTF probing).
> 
> [ ... ]
> 
>> +	cg = cgroup_setup_and_join("iocost_pairing");
>> +	if (!ASSERT_GE(cg, 0, "setup_cg")) {
>> +		iocost_model__destroy(skel);
>> +		nullb_remove(IOCOST_NULLB);
>> +		return;
>> +	}
> 
> [ ... ]
> 
>> +		ASSERT_OK(join_cgroup("iocost_pairing"), "join_pairing");
>> +		snprintf(qbuf, sizeof(qbuf), "%s 1000", dev);
>> +		ASSERT_OK(write_cgroup_file("iocost_pairing", "io.weight",
>> +				     qbuf), "weight_pairing");
>> +		snprintf(qbuf, sizeof(qbuf),
>> +			 "%s enable=1 ctrl=user min=100.00 max=100.00", dev);
>> +		ASSERT_OK(configfs_write("/sys/fs/cgroup/io.cost.qos", qbuf),
>> +			  "qos_enable");
>> +
>> +		devfd = open(path, O_RDONLY);
>> +		if (!ASSERT_OK(devfd < 0 ? -errno : 0, "open_dev_priced")) {
>> +			iocost_model__destroy(skel);
>> +			snprintf(qbuf, sizeof(qbuf), "%s enable=0", dev);
>> +			(void)configfs_write("/sys/fs/cgroup/io.cost.qos", qbuf);
>> +			nullb_remove(IOCOST_NULLB);
>> +			cleanup_cgroup_environment();
>> +			return;
>> +		}
>> +		ASSERT_OK(read_cost_usage("iocost_pairing",
>> +					  dev, &u1), "cost_usage_pre");
>> +		for (i = 0; i < 500; i++)
>> +			(void)read(devfd, buf, sizeof(buf));
>> +		close(devfd);
>> +		ASSERT_GT(skel->bss->calc_costs, 0, "model_priced");
>> +		/*
>> +		 * the usage stat is flushed by the iocost timer, give
>> +		 * the current period a chance to end before reading
>> +		 */
>> +		sleep(3);
>> +		ASSERT_OK(read_cost_usage("iocost_pairing",
>> +					  dev, &u2), "cost_usage_post");
> 
> [Severity: Medium]
> Does passing "iocost_pairing" (and "iocost_ms" in
> serial_test_iocost_model_streams() below) without a leading slash to
> cgroup_setup_and_join(), join_cgroup(), write_cgroup_file(), and
> read_cost_usage()->read_cgroup_file() leak the test cgroups in the root cgroup
> hierarchy?
> 
> In tools/testing/selftests/bpf/cgroup_helpers.c, format_cgroup_path_pid()
> formats paths without inserting a slash before path:
> 
>     #define format_cgroup_path_pid(buf, path, pid) \
>         snprintf(buf, sizeof(buf), "%s%s%d%s", CGROUP_MOUNT_PATH, \
>         CGROUP_WORK_DIR, pid, path)
> 
> Without a leading slash, this creates
> /mnt/cgroup-test-work-dir<pid>iocost_pairing as a sibling of
> /mnt/cgroup-test-work-dir<pid> rather than a child inside it. When
> cleanup_cgroup_environment() runs, __cleanup_cgroup_environment() only runs
> nftw() on /mnt/cgroup-test-work-dir<pid>, leaving the sibling cgroup
> directories and their associated kernel cgroup, blkcg, and cgroup storage
> allocated after the test finishes. Should these relative cgroup paths be
> "/iocost_pairing" and "/iocost_ms"?
> 
> [ ... ]
> 
>> +	err = iocost_ms__load(skel);
>> +	if (!ASSERT_OK(err, "skel_load")) {
>> +		cleanup_cgroup_environment();
>> +		iocost_ms__destroy(skel);
>> +		nullb_remove(IOCOST_NULLB);
>> +		return;
>> +	}
>> +
>> +	err = iocost_ms__attach(skel);
>> +	if (ASSERT_OK(err, "attach")) {
>> +		err = readback_model(dev, model, sizeof(model));
>> +		if (ASSERT_OK(err, "readback"))
>> +			ASSERT_STREQ(model, "bpf", "model_bpf");
>> +
>> +		/*
>> +		 * drive IO through the model: enable the controller, price
>> +		 * from a cgroup, and check the model actually charged them
>> +		 */
>> +		cg = cgroup_setup_and_join("iocost_ms");
>> +		if (!ASSERT_GE(cg, 0, "setup_cg_ms")) {
>> +			iocost_ms__destroy(skel);
>> +			nullb_remove(IOCOST_NULLB);
>> +			cleanup_cgroup_environment();
>> +			return;
>> +		}
> 
> [ ... ]
> 
>> +	}
>> +
>> +	iocost_ms__destroy(skel);
>> +	nullb_remove(IOCOST_NULLB);
>> +	cleanup_cgroup_environment();
>> +}
> 
> [Severity: Low]
> In serial_test_iocost_model_streams(), cgroup_setup_and_join() is only called
> inside the if (ASSERT_OK(err, "attach")) block, and it already calls
> cleanup_cgroup_environment() internally if it fails.
> 
> If iocost_ms__load() fails, if iocost_ms__attach() fails, or if
> cgroup_setup_and_join() fails, won't calling cleanup_cgroup_environment() in
> those paths attempt to clean up an uninitialized or already-cleaned cgroup
> environment and log spurious errors for /mnt/cgroup.procs?
> 
> [1] https://lore.kernel.org/r/20261028143534.135968-4-cui.tao@linux.dev
> [2] https://lore.kernel.org/r/b69d96a507aec382c279d62cfb038fa9@kernel.org
> 


  reply	other threads:[~2026-11-04  3:15 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 13:26 [RFC PATCH v12 0/4] blk-iocost: BPF struct_ops cost model Tao Cui
2026-11-03 13:26 ` [RFC PATCH v12 1/4] blk-iocost: add BPF struct_ops cost model support Tao Cui
2026-11-03 13:51   ` sashiko-bot
2026-11-04  3:10     ` Tao Cui
2026-11-03 16:51   ` Alexei Starovoitov
2026-11-04  3:08     ` Tao Cui
2026-11-03 13:26 ` [RFC PATCH v12 2/4] blk-iocost: add iocost_ioc_tick tracepoint for per-period device summary Tao Cui
2026-11-03 14:07   ` bot+bpf-ci
2026-11-04  3:12     ` Tao Cui
2026-11-03 13:26 ` [RFC PATCH v12 3/4] selftests/bpf: add iocost cost model test Tao Cui
2026-11-03 14:07   ` bot+bpf-ci
2026-11-04  3:13     ` Tao Cui
2026-11-03 14:16   ` sashiko-bot
2026-11-04  3:15     ` Tao Cui [this message]
2026-11-03 13:26 ` [RFC PATCH v12 4/4] docs: cgroup-v2: document the iocost BPF cost model attachment Tao Cui

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=fa7b359c-12a0-4801-97ee-5c11052c6a30@linux.dev \
    --to=cui.tao@linux.dev \
    --cc=bpf@vger.kernel.org \
    --cc=hannes@cmpxchg.org \
    --cc=mkoutny@suse.com \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=tj@kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox