Linux cgroups development
 help / color / mirror / Atom feed
From: Tao Cui <cui.tao@linux.dev>
To: tj@kernel.org, josef@toxicopanda.com, axboe@kernel.dk
Cc: 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, cui.tao@linux.dev,
	Tao Cui <cuitao@kylinos.cn>
Subject: [RFC PATCH 3/8] blk-iocost: implement BPF struct_ops registration
Date: Tue,  8 Sep 2026 18:01:38 +0800	[thread overview]
Message-ID: <20260908100143.47598-4-cui.tao@linux.dev> (raw)
In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev>

From: Tao Cui <cuitao@kylinos.cn>

Register the iocost_model_ops struct_ops type: at most one model
system-wide (EBUSY otherwise), called under RCU from the submit/merge
path.  Registration and unregistration are serialized with a mutex
because the caller only holds the per-map lock.  Includes the
verifier ops, CFI stubs and late_initcall registration.  A model
returning 0 makes the caller fall back to the builtin formula, which
keeps a partial model from making the IO types it does not handle
free.

The verifier allows the base helper set, so models can use maps for
per-cgroup state keyed by iocg_id.  The model runs in the submit path
under RCU and must not sleep.

No code calls the registered model yet; the dispatch hook follows.

Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
 block/Makefile         |   1 +
 block/blk-iocost-bpf.c | 152 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 153 insertions(+)
 create mode 100644 block/blk-iocost-bpf.c

diff --git a/block/Makefile b/block/Makefile
index e7bd320e3d697..ee5cebeea006f 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -39,3 +39,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION)	+= blk-crypto.o blk-crypto-profile.o \
 					   blk-crypto-sysfs.o
 obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK)	+= blk-crypto-fallback.o
 obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED)	+= holder.o
+obj-$(CONFIG_BLK_CGROUP_IOCOST_BPF)	+= blk-iocost-bpf.o
diff --git a/block/blk-iocost-bpf.c b/block/blk-iocost-bpf.c
new file mode 100644
index 0000000000000..0c34f56ded71d
--- /dev/null
+++ b/block/blk-iocost-bpf.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Tao Cui */
+
+/*
+ * blk-iocost: BPF struct_ops plumbing for pluggable cost models.
+ *
+ * Registers the "iocost_model_ops" struct_ops type.  At most one model
+ * can be registered at a time; devices opt in per-queue with
+ * "echo $DEV ctrl=bpf > io.cost.model".  Devices without a registered
+ * model keep using the builtin linear model.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bpf.h>
+#include <linux/bpf_verifier.h>
+#include <linux/btf.h>
+#include <linux/blk-iocost.h>
+
+/* 1s of device time; upper bound on a single IO's chargeable cost */
+#define IOCOST_BPF_MAX_COST	(1ULL << 37)
+
+static struct bpf_struct_ops bpf_iocost_model_ops;
+
+static DEFINE_MUTEX(iocost_bpf_reg_lock);
+static struct iocost_model_ops __rcu *iocost_bpf_model;
+static DEFINE_STATIC_KEY_FALSE(iocost_bpf_key);
+
+bool iocost_bpf_model_registered(void)
+{
+	return static_branch_unlikely(&iocost_bpf_key);
+}
+
+bool iocost_bpf_calc_cost(u64 op, u64 nbytes, u64 sector, u64 cursor,
+			  u64 iocg_id, u64 flags, u64 *costp)
+{
+	const struct iocost_model_ops *ops;
+	u64 cost;
+
+	if (!static_branch_unlikely(&iocost_bpf_key))
+		return false;
+
+	rcu_read_lock();
+	ops = rcu_dereference(iocost_bpf_model);
+	if (!ops) {
+		rcu_read_unlock();
+		return false;
+	}
+	cost = ops->calc_cost(op, nbytes, sector, cursor, iocg_id, flags);
+	rcu_read_unlock();
+
+	if (!cost)
+		return false;
+
+	*costp = min(cost, IOCOST_BPF_MAX_COST);
+	return true;
+}
+
+static int bpf_iocost_model_init(struct btf *btf)
+{
+	s32 type_id;
+
+	type_id = btf_find_by_name_kind(btf, "iocost_model_ops", BTF_KIND_STRUCT);
+	if (type_id < 0)
+		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)
+{
+	return 0;
+}
+
+static bool bpf_iocost_is_valid_access(int off, int size,
+				       enum bpf_access_type type,
+				       const struct bpf_prog *prog,
+				       struct bpf_insn_access_aux *info)
+{
+	return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
+}
+
+static const struct bpf_func_proto *
+bpf_iocost_get_func_proto(enum bpf_func_id func_id,
+			  const struct bpf_prog *prog)
+{
+	return bpf_base_func_proto(func_id, prog);
+}
+
+static const struct bpf_verifier_ops bpf_iocost_verifier_ops = {
+	.get_func_proto = bpf_iocost_get_func_proto,
+	.is_valid_access = bpf_iocost_is_valid_access,
+};
+
+static int bpf_iocost_reg(void *kdata, struct bpf_link *link)
+{
+	struct iocost_model_ops *new_ops = kdata;
+
+	if (!new_ops->calc_cost)
+		return -EINVAL;
+
+	int ret = 0;
+
+	/* the caller only holds the per-map lock, so serialize here */
+	mutex_lock(&iocost_bpf_reg_lock);
+	if (rcu_access_pointer(iocost_bpf_model))
+		ret = -EBUSY;
+	else {
+		static_branch_inc(&iocost_bpf_key);
+		rcu_assign_pointer(iocost_bpf_model, new_ops);
+	}
+	mutex_unlock(&iocost_bpf_reg_lock);
+	return ret;
+}
+
+static void bpf_iocost_unreg(void *kdata, struct bpf_link *link)
+{
+	mutex_lock(&iocost_bpf_reg_lock);
+	if (rcu_access_pointer(iocost_bpf_model) == kdata) {
+		rcu_assign_pointer(iocost_bpf_model, NULL);
+		static_branch_dec(&iocost_bpf_key);
+	}
+	mutex_unlock(&iocost_bpf_reg_lock);
+}
+
+static u64 bpf_iocost_calc_cost_stub(u64 op, u64 nbytes, u64 sector,
+				     u64 cursor, u64 iocg_id, u64 flags)
+{
+	return 0;
+}
+
+static struct iocost_model_ops __bpf_ops_iocost_model_ops = {
+	.calc_cost = bpf_iocost_calc_cost_stub,
+};
+
+static struct bpf_struct_ops bpf_iocost_model_ops = {
+	.verifier_ops = &bpf_iocost_verifier_ops,
+	.init = bpf_iocost_model_init,
+	.init_member = bpf_iocost_init_member,
+	.reg = bpf_iocost_reg,
+	.unreg = bpf_iocost_unreg,
+	.name = "iocost_model_ops",
+	.cfi_stubs = &__bpf_ops_iocost_model_ops,
+	.owner = THIS_MODULE,
+};
+
+static int __init bpf_iocost_init(void)
+{
+	return register_bpf_struct_ops(&bpf_iocost_model_ops, iocost_model_ops);
+}
+late_initcall(bpf_iocost_init);
-- 
2.43.0


  parent reply	other threads:[~2026-09-08 10:02 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-08 10:01 [RFC PATCH 0/8] blk-iocost: BPF struct_ops cost model Tao Cui
2026-09-08 10:01 ` [RFC PATCH 1/8] blk-iocost: add iocost_ioc_tick tracepoint for per-period device summary Tao Cui
2026-09-08 10:01 ` [RFC PATCH 2/8] blk-iocost: define iocost_model_ops cost model interface Tao Cui
2026-09-08 20:31   ` Tejun Heo
2026-09-08 10:01 ` Tao Cui [this message]
2026-09-08 20:31   ` [RFC PATCH 3/8] blk-iocost: implement BPF struct_ops registration Tejun Heo
2026-09-08 10:01 ` [RFC PATCH 4/8] blk-iocost: dispatch cost calculation to registered BPF model Tao Cui
2026-09-08 20:31   ` Tejun Heo
2026-09-08 10:01 ` [RFC PATCH 5/8] blk-iocost: add ctrl=bpf per-device opt-in Tao Cui
2026-09-08 10:01 ` [RFC PATCH 6/8] selftests/bpf: add iocost cost model test Tao Cui
2026-09-08 20:31   ` Tejun Heo
2026-09-08 10:01 ` [RFC PATCH 7/8] selftests/bpf: add multi-stream sequentiality example model Tao Cui
2026-09-08 20:31   ` Tejun Heo
2026-09-08 10:01 ` [RFC PATCH 8/8] docs: cgroup-v2: document io.cost ctrl=bpf option Tao Cui
2026-09-08 20:31 ` [RFC PATCH 0/8] blk-iocost: BPF struct_ops cost model Tejun Heo
2026-09-09 13:12   ` 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=20260908100143.47598-4-cui.tao@linux.dev \
    --to=cui.tao@linux.dev \
    --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