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
>
next prev parent 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 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.