mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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
>> +}


  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®