All of lore.kernel.org
 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: 11+ 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  7:49   ` sashiko-bot
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  8:04   ` sashiko-bot
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  8:11   ` sashiko-bot
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 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.