From: Tao Cui <cui.tao@linux.dev>
To: Amery Hung <ameryhung@gmail.com>
Cc: cui.tao@linux.dev, tj@kernel.org, josef@toxicopanda.com,
axboe@kernel.dk, cgroups@vger.kernel.org,
linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
bpf@vger.kernel.org, andrii@kernel.org, ast@kernel.org,
daniel@iogearbox.net, linux-kselftest@vger.kernel.org,
cuitao@kylinos.cn
Subject: Re: [RFC PATCH v3 1/5] blk-iocost: add BPF struct_ops cost model support
Date: Tue, 15 Sep 2026 10:47:57 +0800 [thread overview]
Message-ID: <04c3ccfb-75e7-484a-b99c-ad68db19153d@linux.dev> (raw)
In-Reply-To: <CAMB2axNkTS-UuH-KctiY+5qh-JZZkKsiyQr_KOnGJt1bPoL3eA@mail.gmail.com>
Hi Amery,
Thanks for the review. These are all addressed in the upcoming v4,
inline below.
在 2026/9/15 07:02, Amery Hung 写道:
> I mostly looked from a struct_ops perspective.
>
>> +
>> +static const struct bpf_func_proto *
>> +bpf_iocost_get_func_proto(enum bpf_func_id func_id,
>> + const struct bpf_prog *prog)
>> +{
>> + switch (func_id) {
>> +#ifdef CONFIG_CGROUPS
>> + case BPF_FUNC_cgrp_storage_get:
>> + return &bpf_cgrp_storage_get_proto;
>
> This seems redundant. BPF_FUNC_cgrp_storage_get should already be part
> of bpf_base_func_proto.
Agreed. get_func_proto() now just delegates to bpf_base_func_proto().
I kept the callback itself, since the verifier resolves helpers
through ops->get_func_proto() and there is no NULL fallback.
>
>> +#endif
>> + default:
>> + return bpf_base_func_proto(func_id, prog);
>> + }
>> +}
>> +
>> +static int bpf_iocost_check_member(const struct btf_type *t,
>> + const struct btf_member *member,
>> + const struct bpf_prog *prog)
>> +{
>> + /* calc_cost() is called with RCU read lock held */
>> + if (prog->sleepable)
>> + return -EINVAL;
>> + return 0;
>> +}
>> +
>> +static int bpf_iocost_init_member(const struct btf_type *t,
>> + const struct btf_member *member,
>> + void *kdata, const void *udata)
>> +{
>> + struct iocost_model_ops *ops = kdata;
>> + const struct iocost_model_ops *uops = udata;
>> + u32 moff = __btf_member_bit_offset(t, member) / 8;
>> +
>> + switch (moff) {
>> + case offsetof(struct iocost_model_ops, name):
>> + if (bpf_obj_name_cpy(ops->name, uops->name,
>> + sizeof(ops->name)) <= 0)
>> + return -EINVAL;
>> + return 1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static u64 bpf_iocost_calc_cost_stub(u64 opf, u64 nbytes, u64 sector,
>> + struct blkcg *blkcg, u64 model_flags);
>> +
>> +/*
>> + * kdata is seeded from the CFI stubs, so calc_cost is never NULL; a
>> + * model which did not implement it inherits the stub, which prices
>> + * every IO at 0. Compare against the stub to reject it.
>> + */
>> +static int bpf_iocost_validate(void *kdata)
>> +{
>> + struct iocost_model_ops *ops = kdata;
>> +
>> + if (ops->calc_cost == bpf_iocost_calc_cost_stub)
>> + return -EINVAL;
>
> This should check !ops->calc_cost. CFI stubs are used to construct
> trampolines; they are not copied into kdata for callbacks omitted by
> userspace. Also, should the reserved name "linear" be rejected here?
I already reworked the validation to check for a NULL calc_cost(),
matching your observation that omitted callbacks remain NULL rather
than inheriting the CFI stub.
A model named "linear" is now rejected as well.
>
>> + return 0;
>> +}
>> +
>> +static int bpf_iocost_reg(void *kdata, struct bpf_link *link)
>> +{
>> + struct iocost_model_ops *ops = kdata;
>> + struct iocost_bpf_model *m;
>> + int ret = 0;
>> +
>> + if (!bpf_struct_ops_get(ops))
>> + return -ENOENT;
>
> struct_ops core should hold a map reference when calling .reg().
> Calling bpf_struct_ops_get() in reg() and bpf_struct_ops_put() in
> unreg() doesn't seem necessary.
Right, the core already holds the map reference during registration,
so I dropped the bpf_struct_ops_get()/put() calls from .reg()/.unreg().
The per-device binding references remain, so an unregistered but still
bound model keeps its kdata alive.
>
>> +
>> + m = kzalloc(sizeof(*m), GFP_KERNEL);
>> + if (!m) {
>> + bpf_struct_ops_put(ops);
>> + return -ENOMEM;
>> + }
>> + refcount_set(&m->refs, 1);
>> +
>> + mutex_lock(&iocost_bpf_reg_lock);
>> + {
>> + struct iocost_bpf_model *other;
>> +
>> + list_for_each_entry(other, &iocost_bpf_models, list) {
>> + if (!strcmp(other->ops->name, ops->name)) {
>> + ret = -EEXIST;
>> + break;
>> + }
>> + }
>> + }
>> + if (!ret) {
>> + m->ops = ops;
>> + list_add(&m->list, &iocost_bpf_models);
>> + list_add(&m->lifecycle, &iocost_bpf_lifecycle);
>
> This seems to contradict what the changelog says: "blkcg
> online/offline notifications follow the model binding, not the name
> registry"
Right, the implementation didn't match the changelog. The lifecycle
list now follows model binding: a model joins it on the first bind
and leaves on the last unbind.
>
>> + }
>> + mutex_unlock(&iocost_bpf_reg_lock);
>> +
>> + if (ret) {
>> + bpf_struct_ops_put(ops);
>> + kfree(m);
>> + }
>> + return ret;
>> +}
>> +
>> +/*
>> + * Unregistering drops the registration reference. When the last
>> + * reference is gone (no device bound), the node leaves the lifecycle
>> + * list and is freed; otherwise bound devices keep it alive and it
>> + * keeps receiving blkcg online/offline notifications.
>> + */
>> +static void bpf_iocost_unreg(void *kdata, struct bpf_link *link)
>> +{
>> + struct iocost_model_ops *ops = kdata;
>> + struct iocost_bpf_model *m;
>> +
>> + mutex_lock(&iocost_bpf_reg_lock);
>> + m = iocost_bpf_model_lookup(ops);
>> + if (m) {
>> + list_del(&m->list);
>> + if (refcount_dec_and_test(&m->refs)) {
>> + list_del(&m->lifecycle);
>> + kfree(m);
>> + }
>> + }
>> + mutex_unlock(&iocost_bpf_reg_lock);
>> +
>> + bpf_struct_ops_put(ops);
>
> Mentioned above. Doesn't seem to be necessary.
>
> [...]
>
>> +static const struct iocost_model_ops *
>> +ioc_bpf_model_prepare(const char *name)
>> +{
>> +#ifdef CONFIG_BLK_CGROUP_IOCOST_BPF
>> + const struct iocost_model_ops *new = NULL;
>> + int ret;
>> +
>> + if (!name[0])
>> + return NULL;
>> + ret = iocost_bpf_model_get(name, &new);
>
> It looks more straightforward if iocost_bpf_model_get() just return
> the ops ptr and ERR_PTR on error.
iocost_bpf_model_get() now returns either the ops pointer or an
ERR_PTR() directly.
I'll post v4 shortly.
Thanks,
Tao
>
>> + return ret ? ERR_PTR(ret) : new;
>> +#else
>> + return name[0] ? ERR_PTR(-ENOENT) : NULL;
>> +#endif
>> +}
next prev parent reply other threads:[~2026-09-15 2:48 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-14 7:33 [RFC PATCH v3 0/5] blk-iocost: BPF struct_ops cost model Tao Cui
2026-09-14 7:33 ` [RFC PATCH v3 1/5] blk-iocost: add BPF struct_ops cost model support Tao Cui
2026-09-14 23:02 ` Amery Hung
2026-09-15 2:47 ` Tao Cui [this message]
2026-09-14 7:33 ` [RFC PATCH v3 2/5] selftests/bpf: add iocost cost model test Tao Cui
2026-09-14 7:33 ` [RFC PATCH v3 3/5] blk-iocost: add iocost_ioc_tick tracepoint for per-period device summary Tao Cui
2026-09-14 7:33 ` [RFC PATCH v3 4/5] selftests/bpf: add multi-stream sequentiality example model Tao Cui
2026-09-14 7:33 ` [RFC PATCH v3 5/5] docs: cgroup-v2: document io.cost model=<name> binding 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=04c3ccfb-75e7-484a-b99c-ad68db19153d@linux.dev \
--to=cui.tao@linux.dev \
--cc=ameryhung@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=axboe@kernel.dk \
--cc=bpf@vger.kernel.org \
--cc=cgroups@vger.kernel.org \
--cc=cuitao@kylinos.cn \
--cc=daniel@iogearbox.net \
--cc=josef@toxicopanda.com \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--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
all inboxes | Powered by JetHome®