From nobody Fri Sep 25 21:40:13 2026 Received: from mta1.migadu.com (out-243.mta1.migadu.com [95.215.58.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DDA4A4DBD8B for ; Tue, 8 Sep 2026 10:02:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.243 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861738; cv=none; b=WF37/Qg8/NX+mfE7FITIoOYudCmoHDGF6slHosGBqeRAw1y+dAmfHo0ssZq1TGfvyEkIJ8kGTxA64gIwc+1CRCqH3wkQj8WnShZR5Mq+EonjZg+AZIe5lPHFF/x7BErqbl03+HQ11ZbGZFntlboCUgkKGK8WdLCAerXWEqZ0CUg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861738; c=relaxed/simple; bh=6Er5qYt+ZL2rO4NlQ1VevHRE6P5HGnDAn+0Yr/dg/0g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cvX2ZB6JHsRCAcMZJpMnc2Yp/K28h6WquUn3qccaXRGDI0HZTG/TUeVJIaI3/KOpu7RgFnJFpv3lvyHlxgzd/6PPx/w2qS9IE0SZY2k1VlS0pKYC5+VE8TuZYTpWd1ilzpl9it/luL6OvloObUfIPz/Y9ZSR9BHwgMl7EBSWbcg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=JWvNfjWg; arc=none smtp.client-ip=95.215.58.243 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="JWvNfjWg" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=6Er5qYt+ZL2rO4NlQ1VevHRE6P5HGnDAn+0Yr/dg/0g=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788861733; v=1; x=1789466533; b=JWvNfjWgf7ukrdt4gC6LENfTtGxf6hReZebMLhAlTcqQa2PvtyEQBUDY8MWYY3173C2Fp8OP t+8h7beDta2ewHRVOAXWvVkfB0dA6u2HpmCuaUfMi853g5EVYC4RpLJ02quSXHekNm8cZ7ZAEwD i5XvSTaJDLFWdpL8RJiblY58= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id f280b3f1737e38a0; Tue, 08 Sep 2026 10:02:13 +0000 X-Mizu-Trace-ID: f280b3f1737e38a0 X-Migadu-Flow: FLOW_OUT From: Tao Cui 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 Subject: [RFC PATCH 1/8] blk-iocost: add iocost_ioc_tick tracepoint for per-period device summary Date: Tue, 8 Sep 2026 18:01:36 +0800 Message-ID: <20260908100143.47598-2-cui.tao@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev> References: <20260908100143.47598-1-cui.tao@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Tao Cui The existing iocost tracepoints are state-change driven: vrate_adj fires only when the adjustment logic runs, inuse_* only on surplus state transitions, activate/idle only on cgroup state changes. In a steady state none of them fire. The only other way to observe the controller (period length, vrate, busy level, active cgroup count, device utilization) is iocost_monitor.py, which reads kernel memory through drgn and is not usable in most production environments. Add iocost_ioc_tick, emitted once per period from the tail of ioc_timer_fn() with the overall controller state: period_us, vrate, busy_level, active iocg count, usage percentage and running state. It fires every period the controller runs, including steady states, plus one final tick before the controller goes idle, which makes dormancy (e.g. a device saturated entirely by uncharged IO) directly visible. At the default period this is a couple of events per second per device; the cost is zero while the static key is off. Signed-off-by: Tao Cui --- block/blk-iocost.c | 5 +++++ include/trace/events/iocost.h | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/block/blk-iocost.c b/block/blk-iocost.c index 2745bffcd5eef..c24daa9d72ee9 100644 --- a/block/blk-iocost.c +++ b/block/blk-iocost.c @@ -2238,6 +2238,7 @@ static void ioc_timer_fn(struct timer_list *timer) struct ioc_now now; LIST_HEAD(surpluses); int nr_debtors, nr_shortages =3D 0, nr_lagging =3D 0; + int nr_active =3D 0; u64 usage_us_sum =3D 0; u32 ppm_rthr; u32 ppm_wthr; @@ -2274,6 +2275,8 @@ static void ioc_timer_fn(struct timer_list *timer) u64 vdone, vtime, usage_us; u32 hw_active, hw_inuse; =20 + nr_active++; + /* * Collect unused and wind vtime closer to vnow to prevent * iocgs from accumulating a large amount of budget. @@ -2460,6 +2463,8 @@ static void ioc_timer_fn(struct timer_list *timer) ioc_refresh_vrate(ioc, &now); } =20 + trace_iocost_ioc_tick(ioc, nr_active, usage_us_sum); + spin_unlock_irq(&ioc->lock); } =20 diff --git a/include/trace/events/iocost.h b/include/trace/events/iocost.h index e772b1bc60d60..2b9ff348a4f51 100644 --- a/include/trace/events/iocost.h +++ b/include/trace/events/iocost.h @@ -178,6 +178,46 @@ TRACE_EVENT(iocost_ioc_vrate_adj, ) ); =20 +/* + * Periodic per-device summary, emitted once per period from the tail of + * ioc_timer_fn(). Unlike the state-change events above, this fires every + * period the controller is running, including steady states, and carries + * the overall controller state so basic monitoring doesn't require drgn. + */ +TRACE_EVENT(iocost_ioc_tick, + + TP_PROTO(struct ioc *ioc, int nr_active, u64 usage_us_sum), + + TP_ARGS(ioc, nr_active, usage_us_sum), + + TP_STRUCT__entry ( + __string(devname, ioc_name(ioc)) + __field(u32, period_us) + __field(u64, vrate) + __field(int, busy_level) + __field(int, nr_active) + __field(u32, usage_pct) + __field(int, running) + ), + + TP_fast_assign( + __assign_str(devname); + __entry->period_us =3D ioc->period_us; + __entry->vrate =3D ioc->vtime_base_rate; + __entry->busy_level =3D ioc->busy_level; + __entry->nr_active =3D nr_active; + __entry->usage_pct =3D ioc->period_us ? + div_u64(usage_us_sum * 100, ioc->period_us) : 0; + __entry->running =3D ioc->running; + ), + + TP_printk("[%s] period=3D%uus vrate=3D%llu busy=3D%d active=3D%d usage=3D= %u%% running=3D%d", + __get_str(devname), __entry->period_us, __entry->vrate, + __entry->busy_level, __entry->nr_active, __entry->usage_pct, + __entry->running + ) +); + TRACE_EVENT(iocost_iocg_forgive_debt, =20 TP_PROTO(struct ioc_gq *iocg, const char *path, struct ioc_now *now, --=20 2.43.0 From nobody Fri Sep 25 21:40:13 2026 Received: from mta0.migadu.com (out-140.mta0.migadu.com [91.218.175.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0A7CF49EC5B for ; Tue, 8 Sep 2026 10:02:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.140 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861748; cv=none; b=JntzQrIoRZh87vyemh29hGKZWmEwZteRJkwXOlBweiDpvAnhP6fpBJNNlXDSt9hVW40tm3Y1puX0G96G8Z8zFtPIqvBBWWeFa/tNAEWjSfr8b29LKk0RfFeDvS1sOKn8g4cvPoTNXQQyEX/8V/sUcNY5sS1mwR0Fnd043tlnTDY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861748; c=relaxed/simple; bh=3EOpRkGpyjybDuKlEe8Dm2WpL894O+e4syc8+3J+rKs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZFiWSksyxkJdbaVkSrV4YbUuQhbsJYjs0JtloOoYTDZUPmmDdjRRC0iCut/qWbg2lkWZlW5u0ecAtfdmmG+fbtmrssGkPcqhhU1PnV5akUxj7qWlc72jeClnCKk0LRwUqdyUT9L4Dwovva7GJlpudAfmsPuQ/mQTNK1mCCcK1OY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=YY864aDQ; arc=none smtp.client-ip=91.218.175.140 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="YY864aDQ" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=3EOpRkGpyjybDuKlEe8Dm2WpL894O+e4syc8+3J+rKs=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788861743; v=1; x=1789466543; b=YY864aDQ8ApF0zFToG0dYNGdF4qoOSE+bCJa81VN2QE4H4LehI1OWr48s2PAHSKlJmKz0NnU P8mWLZMCaZIVvTG1iDB6zZuT2o0VQXTRlstCw+hRo/C0+OE4Vnn7e1r9YgnYfsiRxXPeacE7PDF aXWwiahadRC0HFXxHQeVLeCc= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id e678ba84bb062b64; Tue, 08 Sep 2026 10:02:23 +0000 X-Mizu-Trace-ID: e678ba84bb062b64 X-Migadu-Flow: FLOW_OUT From: Tao Cui 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 Subject: [RFC PATCH 2/8] blk-iocost: define iocost_model_ops cost model interface Date: Tue, 8 Sep 2026 18:01:37 +0800 Message-ID: <20260908100143.47598-3-cui.tao@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev> References: <20260908100143.47598-1-cui.tao@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Tao Cui Define the interface for pluggable cost models: a struct_ops with a single calc_cost(op, nbytes, sector, cursor, iocg_id, flags) callback taking scalar arguments only. The return value is vtime (2^37 per second of device time), clamped to 1s per IO by the kernel. A return value of 0 delegates the IO back to the builtin formula, so a model which only handles some IO types cannot make the rest free. iocg_id is the id of the issuing cgroup, so a model can keep per-cgroup state, e.g. for multi-stream sequentiality detection. The id is only valid while the cgroup exists and is recycled after removal, so models must treat it as a transient key; struct_ops callback signatures are frozen once merged, so the identifier is part of the initial interface. The model is called from the IO submission path under RCU and must not sleep. Only the bio-level charging path consults the model; the request-level sizing path keeps using the builtin formula. This is a definition-only patch; the registration infrastructure and dispatch hook follow in subsequent patches. Signed-off-by: Tao Cui --- block/Kconfig | 9 ++++++ include/linux/blk-iocost.h | 56 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 include/linux/blk-iocost.h diff --git a/block/Kconfig b/block/Kconfig index 70e4a66d941ff..bf43be21e00bb 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -231,4 +231,13 @@ config BLK_ERROR_INJECTION =20 source "block/Kconfig.iosched" =20 +config BLK_CGROUP_IOCOST_BPF + bool "Enable BPF pluggable cost model support for the cost IO controller" + depends on BLK_CGROUP_IOCOST && BPF_SYSCALL && BPF_JIT + help + Enabling this option registers the "iocost_model_ops" BPF + struct_ops type, which allows a BPF program to replace the + builtin linear cost model on devices configured with + "ctrl=3Dbpf" through io.cost.model. + endif # BLOCK diff --git a/include/linux/blk-iocost.h b/include/linux/blk-iocost.h new file mode 100644 index 0000000000000..7111b5c03dc36 --- /dev/null +++ b/include/linux/blk-iocost.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_BLK_IOCOST_H +#define _LINUX_BLK_IOCOST_H + +#include + +#ifdef CONFIG_BLK_CGROUP_IOCOST_BPF + +/* + * Pluggable cost model interface for blk-iocost. + * + * A BPF struct_ops implementation registered against "iocost_model_ops" + * replaces the builtin linear cost model on devices configured with + * "ctrl=3Dbpf" through io.cost.model. The model is a pure function of the + * arguments below and returns the cost of the IO in device time units, + * where 1 second of device time equals 2^37 (VTIME_PER_SEC). The + * returned value is clamped by the kernel. A cost of 0 delegates the + * IO back to the builtin formula, so a model which does not handle a + * given IO type cannot make it free. + * + * The iocg_id argument identifies the issuing cgroup (css id) so the + * model can keep per-cgroup state, e.g. for multi-stream sequentiality + * detection. The id is only valid while the cgroup exists: css ids are + * recycled once the cgroup is removed, so models must treat it as a + * transient key and reset state when they observe it reused. Whether + * the interface needs a release(iocg_id) callback for state cleanup is + * an open question. + * + * The model is called from the IO submission path under RCU and must + * not sleep. Only the bio-level charging path consults the model; + * the request-level sizing path (calc_size_vtime_cost()) keeps using + * the builtin formula. + */ + +/* flags for calc_cost() */ +#define IOCOST_COST_F_MERGE (1ULL << 0) /* called from merge path */ + +struct iocost_model_ops { + /* + * @op: REQ_OP_* value (uapi blk_opf.h) + * @nbytes: IO size in bytes + * @sector: starting sector + * @cursor: iocg cursor sector, 0 if none (sequentiality hint) + * @iocg_id: css id of the issuing cgroup + * @flags: IOCOST_COST_F_* + */ + u64 (*calc_cost)(u64 op, u64 nbytes, u64 sector, u64 cursor, + u64 iocg_id, u64 flags); +}; + +bool iocost_bpf_calc_cost(u64 op, u64 nbytes, u64 sector, u64 cursor, + u64 iocg_id, u64 flags, u64 *costp); +bool iocost_bpf_model_registered(void); + +#endif /* CONFIG_BLK_CGROUP_IOCOST_BPF */ +#endif /* _LINUX_BLK_IOCOST_H */ --=20 2.43.0 From nobody Fri Sep 25 21:40:13 2026 Received: from mta1.migadu.com (out-12.mta1.migadu.com [95.215.58.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A0D1F4CC265 for ; Tue, 8 Sep 2026 10:02:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861760; cv=none; b=iyp+fBBc1dAfeYZon2X2S6Lp7cBxb3DQE8LMPh0GI2dOXvCfZkNV64OFz8vVWJCJim/tP0JZOyyEYzFthHMHbOS9Mrn28BmlDpFIXGS/rS+hPx819z/yAzo8t+JjRZ7+mlMz0N/QPCUFjjvyoQ6eS6kwve0BOnF4Vz5Z9RZjFSY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861760; c=relaxed/simple; bh=wWqqyXxBeroIFBeIbRhs73ts8SnHGnu+jeiXZcy34Mk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HELnGJFweIGbSvtEnR+6/BfcsjTNUPWtLAIQiEqsQzDQb7Gv9SWbPo0FhpVrZBXTFacmHGFNfeYfJDAgQMLjmj/vvQUpW4FmB3dIAS2pCJHDObRbIkrUzsvArP7pWcGTWBnwQ76NND+BAeHgMKZYY8wtgNoj11yWdOHYmx2Hm/o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=BBPg2zbv; arc=none smtp.client-ip=95.215.58.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="BBPg2zbv" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=wWqqyXxBeroIFBeIbRhs73ts8SnHGnu+jeiXZcy34Mk=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788861755; v=1; x=1789466555; b=BBPg2zbvP4e7oWrVOPTSaJmbIJ57mrXJ7/08yRBMg1dCllozSjG5wr7EaDQSRebu/V0N2O4t R74RMiTVf9NIjJHjwQx8jDtYl5aUf9nalTsVq3MzlTnSXjfYQzm2RwO9Mu19/BZOcwzF1HmpHYT lDw8GtuCiWoaOEYiQaFl3DtM= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id e2c0af394d354957; Tue, 08 Sep 2026 10:02:35 +0000 X-Mizu-Trace-ID: e2c0af394d354957 X-Migadu-Flow: FLOW_OUT From: Tao Cui 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 Subject: [RFC PATCH 3/8] blk-iocost: implement BPF struct_ops registration Date: Tue, 8 Sep 2026 18:01:38 +0800 Message-ID: <20260908100143.47598-4-cui.tao@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev> References: <20260908100143.47598-1-cui.tao@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Tao Cui 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 --- 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) +=3D blk-crypto.o blk= -crypto-profile.o \ blk-crypto-sysfs.o obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) +=3D blk-crypto-fallback.o obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) +=3D holder.o +obj-$(CONFIG_BLK_CGROUP_IOCOST_BPF) +=3D 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=3Dbpf > io.cost.model". Devices without a registered + * model keep using the builtin linear model. + */ +#include +#include +#include +#include +#include +#include +#include + +/* 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 =3D rcu_dereference(iocost_bpf_model); + if (!ops) { + rcu_read_unlock(); + return false; + } + cost =3D ops->calc_cost(op, nbytes, sector, cursor, iocg_id, flags); + rcu_read_unlock(); + + if (!cost) + return false; + + *costp =3D min(cost, IOCOST_BPF_MAX_COST); + return true; +} + +static int bpf_iocost_model_init(struct btf *btf) +{ + s32 type_id; + + type_id =3D btf_find_by_name_kind(btf, "iocost_model_ops", BTF_KIND_STRUC= T); + 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 =3D { + .get_func_proto =3D bpf_iocost_get_func_proto, + .is_valid_access =3D bpf_iocost_is_valid_access, +}; + +static int bpf_iocost_reg(void *kdata, struct bpf_link *link) +{ + struct iocost_model_ops *new_ops =3D kdata; + + if (!new_ops->calc_cost) + return -EINVAL; + + int ret =3D 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 =3D -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) =3D=3D 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 =3D { + .calc_cost =3D bpf_iocost_calc_cost_stub, +}; + +static struct bpf_struct_ops bpf_iocost_model_ops =3D { + .verifier_ops =3D &bpf_iocost_verifier_ops, + .init =3D bpf_iocost_model_init, + .init_member =3D bpf_iocost_init_member, + .reg =3D bpf_iocost_reg, + .unreg =3D bpf_iocost_unreg, + .name =3D "iocost_model_ops", + .cfi_stubs =3D &__bpf_ops_iocost_model_ops, + .owner =3D 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); --=20 2.43.0 From nobody Fri Sep 25 21:40:13 2026 Received: from mta1.migadu.com (out-18.mta1.migadu.com [95.215.58.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 533544D2EDA for ; Tue, 8 Sep 2026 10:02:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861771; cv=none; b=Iq+xOv5So9aSDtzBRdjXHghvUTlMRlsgxMyxgHXz8YELPgWkXA8BihiiEQBXgJhzVuLBvLcumw0WPWrirRSkxMhsuvDn6G8wAqmdM09tdM/b/Dpy5snb9g4w7QOalWcjBu2nXlhvaSn87n0/6lQ70MZwsbQxif3e/7uFFGC8fmw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861771; c=relaxed/simple; bh=FWmTXavAEUQXQQ4OFK6vpxzFjyhwrttNU0kXo7PDdAo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UxU4KguantXNm8xNq6RAy8XvLuhY31/R9lHvEfmozzxgrvgd7kOyr5y0mlsSZimQhsGfdlT9hM+rJeVCQCDCTrKR92/M1FYiWmTbXr2kIGjsRjO5ukSUOffrXPT1LYQNcIQZQwM8b1ip0DgnesXm6vqcxoHMYs/u2X7cmp0NxRU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=lc28t/me; arc=none smtp.client-ip=95.215.58.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="lc28t/me" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=FWmTXavAEUQXQQ4OFK6vpxzFjyhwrttNU0kXo7PDdAo=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788861766; v=1; x=1789466566; b=lc28t/meusKDHouTXP/HJ2d24QFJIztAQBrN4ALvCWiyzqnRntSYlEdJ51ypg6Q0vKKyG9ee SaFgvJcf47Bh/akHNkL/ctIkqPSMeqm72DahrIsFW/fpqe8gDbNxKbNRLQHz3gSBK0S0ICFpqVI 4N4ECBq5dwxnVfEd0rBedLvo= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id dea8bc1b366ba06c; Tue, 08 Sep 2026 10:02:46 +0000 X-Mizu-Trace-ID: dea8bc1b366ba06c X-Migadu-Flow: FLOW_OUT From: Tao Cui 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 Subject: [RFC PATCH 4/8] blk-iocost: dispatch cost calculation to registered BPF model Date: Tue, 8 Sep 2026 18:01:39 +0800 Message-ID: <20260908100143.47598-5-cui.tao@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev> References: <20260908100143.47598-1-cui.tao@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Tao Cui Add the bpf_cost_model field to struct ioc and the dispatch hook in calc_vtime_cost(): when a BPF model is registered and the device has opted in (bpf_cost_model set), call the model's calc_cost instead of the builtin formula. If no model is registered anymore the builtin formula is used as fallback. The user interface to set bpf_cost_model follows. Signed-off-by: Tao Cui --- block/blk-iocost.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/block/blk-iocost.c b/block/blk-iocost.c index c24daa9d72ee9..bdb4bab86a116 100644 --- a/block/blk-iocost.c +++ b/block/blk-iocost.c @@ -177,6 +177,7 @@ #include #include #include +#include #include #include #include @@ -445,6 +446,7 @@ struct ioc { int autop_idx; bool user_qos_params:1; bool user_cost_model:1; + bool bpf_cost_model; }; =20 struct iocg_pcpu_stat { @@ -2578,6 +2580,23 @@ static u64 calc_vtime_cost(struct bio *bio, struct i= oc_gq *iocg, bool is_merge) { u64 cost; =20 +#ifdef CONFIG_BLK_CGROUP_IOCOST_BPF + struct ioc *ioc =3D iocg->ioc; + + if (READ_ONCE(ioc->bpf_cost_model)) { + u64 bpf_cost; + + if (iocost_bpf_calc_cost(bio_op(bio), + bio->bi_iter.bi_size, + bio->bi_iter.bi_sector, + iocg->cursor, + iocg_to_blkg(iocg)->blkcg->css.id, + is_merge ? IOCOST_COST_F_MERGE : 0, + &bpf_cost)) + return bpf_cost; + } +#endif + calc_vtime_cost_builtin(bio, iocg, is_merge, &cost); return cost; } --=20 2.43.0 From nobody Fri Sep 25 21:40:13 2026 Received: from mta1.migadu.com (out-22.mta1.migadu.com [95.215.58.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A585551C341 for ; Tue, 8 Sep 2026 10:03:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.22 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861782; cv=none; b=TEqk9+LQdY0EPRCq2NFfdaVMI2iNt0Lzh3ROvRa2FkARi+9zDcFqSwaDg4Qne2eqOAxHkTBXmbf56Ot4UofKAY6qmaJH8tllUKfqC+KnDBA7MJ4GgoaHqHzF8OEULIlShQfFbIFz0ud/BLBZ6sZBvg7KgqU8VlC8AoPLIlOVtqY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861782; c=relaxed/simple; bh=zn+RVb0VJos1Q3KLxQmsn4wBd6PEVCA2uEn2zoudQF8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hKMUEHP7Edcurtpx47zlzZPprSITyyzzKZ7uvWJghcfhiA5ZRbZyHXn0ogC4CjdDYfnJQd0sTaefgrJsHGO/STEGE4tHCE8T62DMYr7xUIYFt2Zt/g7RfN8pkqO9C9FS2x2U8Z7nQastxyUkvWX5P9pj9T4rntwrJIhSfnz2mro= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=uBrIayhg; arc=none smtp.client-ip=95.215.58.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="uBrIayhg" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=zn+RVb0VJos1Q3KLxQmsn4wBd6PEVCA2uEn2zoudQF8=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788861778; v=1; x=1789466578; b=uBrIayhg1qvLnKlAUemR5sWSSxvzazsdFvetTvEU27PAKa3tiQFtW0bUKpS++U5qoxgt4TL2 Tuh6txrY8dpVr7IK5iV3sb48eZgD5rRyi/rszqufsvjKLB5YEkkuHAlCWJ0AGzNbU3ZXQthCJU0 LbEXDq1+4caI5Ogq/H7UCdQM= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id a502f24c1c2c7d6a; Tue, 08 Sep 2026 10:02:58 +0000 X-Mizu-Trace-ID: a502f24c1c2c7d6a X-Migadu-Flow: FLOW_OUT From: Tao Cui 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 Subject: [RFC PATCH 5/8] blk-iocost: add ctrl=bpf per-device opt-in Date: Tue, 8 Sep 2026 18:01:40 +0800 Message-ID: <20260908100143.47598-6-cui.tao@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev> References: <20260908100143.47598-1-cui.tao@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Tao Cui Add "bpf" to the ctrl=3D options in io.cost.model. Writing "$dev ctrl=3Dbpf" to io.cost.model switches the device to the registered BPF cost model (if any is registered); ctrl=3Dauto or ctrl=3Duser switches back to the builtin model. Signed-off-by: Tao Cui --- block/blk-iocost.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/block/blk-iocost.c b/block/blk-iocost.c index bdb4bab86a116..a58c16fcb29fa 100644 --- a/block/blk-iocost.c +++ b/block/blk-iocost.c @@ -3465,7 +3465,8 @@ static u64 ioc_cost_model_prfill(struct seq_file *sf, seq_printf(sf, "%s ctrl=3D%s model=3Dlinear " "rbps=3D%llu rseqiops=3D%llu rrandiops=3D%llu " "wbps=3D%llu wseqiops=3D%llu wrandiops=3D%llu\n", - dname, ioc->user_cost_model ? "user" : "auto", + dname, ioc->bpf_cost_model ? "bpf" : + ioc->user_cost_model ? "user" : "auto", u[I_LCOEF_RBPS], u[I_LCOEF_RSEQIOPS], u[I_LCOEF_RRANDIOPS], u[I_LCOEF_WBPS], u[I_LCOEF_WSEQIOPS], u[I_LCOEF_WRANDIOPS]); spin_unlock_irq(&ioc->lock); @@ -3505,7 +3506,7 @@ static ssize_t ioc_cost_model_write(struct kernfs_ope= n_file *of, char *input, unsigned int memflags; struct ioc *ioc; u64 u[NR_I_LCOEFS]; - bool user; + bool user, bpf; char *body, *p; int ret; =20 @@ -3536,6 +3537,7 @@ static ssize_t ioc_cost_model_write(struct kernfs_ope= n_file *of, char *input, spin_lock_irq(&ioc->lock); memcpy(u, ioc->params.i_lcoefs, sizeof(u)); user =3D ioc->user_cost_model; + bpf =3D ioc->bpf_cost_model; =20 ret =3D -EINVAL; =20 @@ -3551,11 +3553,20 @@ static ssize_t ioc_cost_model_write(struct kernfs_o= pen_file *of, char *input, switch (match_token(p, cost_ctrl_tokens, args)) { case COST_CTRL: match_strlcpy(buf, &args[0], sizeof(buf)); - if (!strcmp(buf, "auto")) + if (!strcmp(buf, "auto")) { user =3D false; - else if (!strcmp(buf, "user")) + bpf =3D false; + } else if (!strcmp(buf, "user")) { user =3D true; - else + bpf =3D false; +#ifdef CONFIG_BLK_CGROUP_IOCOST_BPF + } else if (!strcmp(buf, "bpf")) { + if (!iocost_bpf_model_registered()) + goto unlock; + user =3D false; + bpf =3D true; +#endif + } else goto unlock; continue; case COST_MODEL: @@ -3580,6 +3591,7 @@ static ssize_t ioc_cost_model_write(struct kernfs_ope= n_file *of, char *input, } else { ioc->user_cost_model =3D false; } + WRITE_ONCE(ioc->bpf_cost_model, bpf); ioc_refresh_params(ioc, true); =20 ret =3D 0; --=20 2.43.0 From nobody Fri Sep 25 21:40:13 2026 Received: from mta0.migadu.com (out-172.mta0.migadu.com [91.218.175.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C90C5237B3 for ; Tue, 8 Sep 2026 10:03:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861793; cv=none; b=YBM1ryp5WNBl+MkimTrY9bn6eeobsnOpcWPHgpiV6EckOQ4q9YHlxGkbkXNt03a+SWcOcN75NCJayUi3Hfdo3AQ0itEzgl3Ggobv1m/N6r1B1yTAixCwCygcB7twiao5avJXXnwU46QFw/5HIzKOrkSUPRJTThYBPtuujIrOK20= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861793; c=relaxed/simple; bh=r4FFykHx2l0Mym4QKl1EfWhMWo17b+iIf/S75GX9C8I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hwDr6k4KyjXoGNxIfacwtlhFYo1j5nD5GwvpySXFC9wJKaZuwGtbF5Q9vCb1FyEpo8TwxJ1tiqfRq2wmd9Hhgbg1BZ0wkxQyJs9Gs7W6B3ZPvVigqgp5yyeUSMXKvtaDXwKYsbhzTQAkJ5f+9y6pvixDnzlxhBk7IUe8Uk16igs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=Hjw1Nxt2; arc=none smtp.client-ip=91.218.175.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="Hjw1Nxt2" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=r4FFykHx2l0Mym4QKl1EfWhMWo17b+iIf/S75GX9C8I=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788861788; v=1; x=1789466588; b=Hjw1Nxt2FlCqtEby6RwyjPsfi6lncRLLubQU5P83G2amqWf8Ng7nmMPT77tiNSEnplLzyeu6 gCVQeMiqQDcWMLvBqgjBDUw+IfOls7MHNOSxzZaN5x1mfojTTxnglofb15rQsEkM5pMLZRD2rIs Me4XMXnBVrfCTZHt8ypyB7No= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id dbfa77b83717f402; Tue, 08 Sep 2026 10:03:08 +0000 X-Mizu-Trace-ID: dbfa77b83717f402 X-Migadu-Flow: FLOW_OUT From: Tao Cui 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 Subject: [RFC PATCH 6/8] selftests/bpf: add iocost cost model test Date: Tue, 8 Sep 2026 18:01:41 +0800 Message-ID: <20260908100143.47598-7-cui.tao@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev> References: <20260908100143.47598-1-cui.tao@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Tao Cui Add an example cost model implementing iocost_model_ops with the builtin linear HDD formula at double cost, and a runner which registers it as a struct_ops, switches a device to ctrl=3Dbpf through io.cost.model and verifies the readback, restoring ctrl=3Dauto afterwards. The runner first checks the negative case: writing ctrl=3Dbpf while no model is registered is rejected. Under the same workload the doubled model should charge exactly twice the builtin model, which makes it a convenient way to verify that accounting goes through the BPF path; the accounting itself needs real IO and is covered by the kernel-side validation described in the cover letter. The runner is skipped unless $IOCOST_TEST_DEV gives a major:minor of a device with iocost enabled. Signed-off-by: Tao Cui --- .../selftests/bpf/prog_tests/iocost_model.c | 124 ++++++++++++++++++ .../selftests/bpf/progs/iocost_model.c | 91 +++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/iocost_model.c create mode 100644 tools/testing/selftests/bpf/progs/iocost_model.c 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..386026241d5b4 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/iocost_model.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Tao Cui */ +#include +#include "iocost_model.skel.h" + +/* + * Switch a device to ctrl=3Dbpf through io.cost.model and verify the + * readback, restoring ctrl=3Dauto afterwards. Returns 0 on success. + * Shared by the example-model tests below. + */ +static int switch_to_bpf(const char *dev) +{ + char path[] =3D "/sys/fs/cgroup/io.cost.model"; + char word[256], line[256], buf[300]; + FILE *fp; + int err, found =3D 0; + + fp =3D fopen(path, "r+"); + if (!fp) { + TH_LOG("open %s: %s", path, strerror(errno)); + return -1; + } + + while (fgets(line, sizeof(line), fp)) { + if (sscanf(line, "%255s", word) =3D=3D 1 && !strcmp(word, dev)) { + found =3D 1; + break; + } + } + if (!found) { + TH_LOG("%s has no iocost line", dev); + fclose(fp); + return -1; + } + + snprintf(buf, sizeof(buf), "%s ctrl=3Dbpf", dev); + err =3D fprintf(fp, "%s\n", buf); + if (err <=3D 0) { + TH_LOG("write ctrl=3Dbpf failed"); + fclose(fp); + return -1; + } + fflush(fp); + + rewind(fp); + found =3D 0; + while (fgets(line, sizeof(line), fp)) { + if (sscanf(line, "%255s", word) =3D=3D 1 && !strcmp(word, dev)) { + found =3D strstr(line, "ctrl=3Dbpf") !=3D NULL; + break; + } + } + if (!found) + TH_LOG("readback does not show ctrl=3Dbpf"); + + fprintf(fp, "%s ctrl=3Dauto\n", dev); + fclose(fp); + return found ? 0 : -1; +} + +static int dev_has_iocost(void) +{ + FILE *fp =3D fopen("/sys/fs/cgroup/io.cost.qos", "r"); + + if (fp) + fclose(fp); + return fp !=3D NULL; +} + +/* + * Register the example cost model and switch a device to ctrl=3Dbpf. + * IO accounting itself is not checked here; it needs a device doing + * real IO under iocost and is covered by the kernel-side validation + * described in the cover letter. + * + * Requires root, cgroup v2 and a device with iocost support. The + * device must be given as major:minor in $IOCOST_TEST_DEV, otherwise + * the test is skipped. + */ +void serial_test_iocost_model(void) +{ + struct iocost_model *skel; + char word[256], *dev; + int err; + + dev =3D getenv("IOCOST_TEST_DEV"); + if (!dev || geteuid() !=3D 0) { + test__skip(); + return; + } + if (!ASSERT_TRUE(dev_has_iocost(), "iocost_mounted")) + return; + + /* negative: ctrl=3Dbpf must be rejected while no model is + * registered, so a typo cannot silently disable cost model + * updates */ + if (sscanf(dev, "%255s", word) !=3D 1) + return; + { + char path[] =3D "/sys/fs/cgroup/io.cost.model"; + char buf[300]; + FILE *fp =3D fopen(path, "w"); + + snprintf(buf, sizeof(buf), "%s ctrl=3Dbpf\n", word); + err =3D 0; + if (fp) { + err =3D fprintf(fp, "%s", buf) <=3D 0; + fclose(fp); + } + ASSERT_TRUE(err, "ctrl_bpf_without_model_rejected"); + } + + skel =3D iocost_model__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_load")) + return; + + /* attaching the struct_ops registers the model; a second + * registration of the same ops would fail with EBUSY */ + err =3D iocost_model__attach(skel); + if (ASSERT_OK(err, "attach")) + ASSERT_OK(switch_to_bpf(dev), "switch_and_readback"); + + iocost_model__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/iocost_model.c b/tools/testi= ng/selftests/bpf/progs/iocost_model.c new file mode 100644 index 0000000000000..70f7987f8eb6a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/iocost_model.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Tao Cui */ +/* + * Example iocost cost model: the builtin linear HDD formula with all + * costs doubled. + * + * The constants mirror what calc_lcoefs() derives from the AUTOP_HDD + * defaults (rbps=3D174019176 rseqiops=3D41708 rrandiops=3D370, w-side + * analog) in vtime units where 1s =3D=3D 2^37, expressed with the same + * round-up divisions so they cannot drift from the kernel, so a + * device switched to ctrl=3Dbpf with this model registered charges + * exactly twice the builtin model under the same workload, which + * makes it a convenient way to verify that accounting goes through + * the BPF path. + */ +#include "vmlinux.h" +#include +#include + +/* VTIME_PER_SEC comes from vmlinux.h (a BTF enum constant) */ +#define IOC_PAGE_SIZE 4096 +#define IOC_SECT_TO_PAGE_SHIFT 3 /* 512B sectors to 4kB pages */ +#define LCOEF_RANDIO_PAGES 4096 /* 16MB seek threshold */ +#define IOCOST_COST_F_MERGE (1ULL << 0) /* not in BTF: a plain macro */ + +/* DIV64_U64_ROUND_UP / DIV_ROUND_UP_ULL equivalents, folded at + * compile time */ +#define RU(x, y) ((x) / (y) + (((x) % (y)) ? 1 : 0)) + +#define RBPS 174019176ULL +#define RSEQIOPS 41708ULL +#define RRANDIOPS 370ULL +#define WBPS 178075866ULL +#define WSEQIOPS 42705ULL +#define WRANDIOPS 378ULL + +#define RPAGE (RU(VTIME_PER_SEC, RU(RBPS, IOC_PAGE_SIZE))) +#define RSEQIO (RU(VTIME_PER_SEC, RSEQIOPS) - RPAGE) +#define RRANDIO (RU(VTIME_PER_SEC, RRANDIOPS) - RPAGE) +#define WPAGE (RU(VTIME_PER_SEC, RU(WBPS, IOC_PAGE_SIZE))) +#define WSEQIO (RU(VTIME_PER_SEC, WSEQIOPS) - WPAGE) +#define WRANDIO (RU(VTIME_PER_SEC, WRANDIOPS) - WPAGE) + +static __always_inline u64 builtin_cost(u64 op, u64 nbytes, u64 sector, + u64 cursor, u64 flags) +{ + u64 pages, seek_pages =3D 0, base, coef_page; + + if (!nbytes) + return 0; + + switch (op) { + case REQ_OP_READ: + base =3D RSEQIO; + coef_page =3D RPAGE; + break; + case REQ_OP_WRITE: + base =3D WSEQIO; + coef_page =3D WPAGE; + break; + default: + return 0; + } + + if (cursor) { + seek_pages =3D sector > cursor ? sector - cursor + : cursor - sector; + seek_pages >>=3D IOC_SECT_TO_PAGE_SHIFT; + if (seek_pages > LCOEF_RANDIO_PAGES) + base =3D (op =3D=3D REQ_OP_READ) ? RRANDIO : WRANDIO; + } + + pages =3D RU(nbytes, IOC_PAGE_SIZE); + if (flags & IOCOST_COST_F_MERGE) + base =3D 0; + return base + pages * coef_page; +} + +SEC("struct_ops") +u64 BPF_PROG(iocost_2x_calc_cost, u64 op, u64 nbytes, u64 sector, + u64 cursor, u64 iocg_id, u64 flags) +{ + return 2 * builtin_cost(op, nbytes, sector, cursor, flags); +} + +SEC(".struct_ops") +struct iocost_model_ops iocost_2x =3D { + .calc_cost =3D (void *)iocost_2x_calc_cost, +}; + +char LICENSE[] SEC("license") =3D "GPL"; --=20 2.43.0 From nobody Fri Sep 25 21:40:13 2026 Received: from mta0.migadu.com (out-178.mta0.migadu.com [91.218.175.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1D3D05013CA for ; Tue, 8 Sep 2026 10:03:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861803; cv=none; b=b2fwE0JPm3rOX2IPiptC9uVYXHwX28bV8uABL32wGRMRmQvJ91Mjpi0GvjVUecaeDCVhMgxsXlaHVgiK2imiIrN6lLNLhwPQ6WE2P1GNevZJzxns/pz6q2ssSeJO1R3JrO2TsRBFpKZtBY6zaQGP8t14KQDH7YGQWiO9OBl8pJM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861803; c=relaxed/simple; bh=RhJmLBVSYcp5nip2MtkY2RhJjeuafeZzdoCjJRShePM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RMspItJfYP6sTon3AsNNKIf9+gbODmew5khA/08tKTtbx9YEMe4coiltuRtGHQ5S/p58r84bcFEaUBjmww/4p2H1oMryv0GD5sPjw9Jz8SIWsOsBXqNC88jXqOMBfRd+I3WTWpGNyLqcMNqA+ANa97KhxH98R3+pjaE3hz9Fy+g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=xfksQNEI; arc=none smtp.client-ip=91.218.175.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="xfksQNEI" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=RhJmLBVSYcp5nip2MtkY2RhJjeuafeZzdoCjJRShePM=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788861799; v=1; x=1789466599; b=xfksQNEIHgkw9CSFyzvYs7CU4LytEcilpuIow4UUDkXb0+E2g3/UOHi+BnOHF/HT041tYvJ4 bRYtVKqoPWpsG+vAoBHgPIRPGo7LYK0oaU1dKR+qxVyprO5ZljKBR8AjdqamY7jbjzXCZo/ZAmg G6WZNwCIIlRB7e8dhCsWwGIo= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 3258799641478f75; Tue, 08 Sep 2026 10:03:19 +0000 X-Mizu-Trace-ID: 3258799641478f75 X-Migadu-Flow: FLOW_OUT From: Tao Cui 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 Subject: [RFC PATCH 7/8] selftests/bpf: add multi-stream sequentiality example model Date: Tue, 8 Sep 2026 18:01:42 +0800 Message-ID: <20260908100143.47598-8-cui.tao@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev> References: <20260908100143.47598-1-cui.tao@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Tao Cui Add a second example cost model which replaces the builtin single-cursor sequentiality heuristic with a per-cgroup table of stream slots keyed by iocg_id: an IO is sequential iff its sector matches the expected next sector of any tracked stream. Interleaved sequential readers in one cgroup keep their own slots instead of ping-ponging a single cursor, and random IO inside a hot window rarely matches a moving expectation. Measured (QEMU, virtio-blk with the HDD profile, 4k IOs, w=3D1000): two sequential readers in one cgroup are priced 2064us/op by the builtin model (judged random) and 23us/op by this model (judged sequential), a throughput recovery from 7.2 to 511 MiB/s; random IO inside an 8M window is priced 22.9us/op by builtin (undercharge) and 2643us/op by this model; single-stream sequential and whole-disk random pricing are unchanged. The runner reuses the switch helper from the first example model. Signed-off-by: Tao Cui --- .../selftests/bpf/prog_tests/iocost_model.c | 31 +++++ tools/testing/selftests/bpf/progs/iocost_ms.c | 122 ++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/iocost_ms.c diff --git a/tools/testing/selftests/bpf/prog_tests/iocost_model.c b/tools/= testing/selftests/bpf/prog_tests/iocost_model.c index 386026241d5b4..2c149f3cd243a 100644 --- a/tools/testing/selftests/bpf/prog_tests/iocost_model.c +++ b/tools/testing/selftests/bpf/prog_tests/iocost_model.c @@ -2,6 +2,7 @@ /* Copyright (c) 2026 Tao Cui */ #include #include "iocost_model.skel.h" +#include "iocost_ms.skel.h" =20 /* * Switch a device to ctrl=3Dbpf through io.cost.model and verify the @@ -122,3 +123,33 @@ void serial_test_iocost_model(void) =20 iocost_model__destroy(skel); } + +/* + * Same check for the multi-stream example model. Only one model can + * be registered at a time, so this test must run separately from + * test_iocost_model; both are serial. + */ +void serial_test_iocost_model_streams(void) +{ + struct iocost_ms *skel; + char *dev; + int err; + + dev =3D getenv("IOCOST_TEST_DEV"); + if (!dev || geteuid() !=3D 0) { + test__skip(); + return; + } + if (!ASSERT_TRUE(dev_has_iocost(), "iocost_mounted")) + return; + + skel =3D iocost_ms__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_load")) + return; + + err =3D iocost_ms__attach(skel); + if (ASSERT_OK(err, "attach")) + ASSERT_OK(switch_to_bpf(dev), "switch_and_readback"); + + iocost_ms__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/iocost_ms.c b/tools/testing/= selftests/bpf/progs/iocost_ms.c new file mode 100644 index 0000000000000..465fd5d5c62e0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/iocost_ms.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Tao Cui */ +/* + * Example multi-stream sequentiality detection cost model. + * + * The builtin model keeps a single cursor per cgroup, so two + * interleaved sequential readers in one cgroup are all priced random + * (measured 89x overcharge, 12.9x throughput collapse), while random + * IO inside a hot window smaller than the 16MB seek threshold is + * priced sequential (measured 107x undercharge). This model replaces + * the single cursor with a per-cgroup table of stream slots, keyed by + * the iocg_id argument: an IO is sequential iff its sector matches the + * expected next sector of any tracked stream. Interleaved streams + * keep their own slots, and windowed random IO rarely matches a + * moving expectation. + */ +#include "vmlinux.h" +#include +#include + +/* VTIME_PER_SEC comes from vmlinux.h (a BTF enum constant) */ +#define IOC_PAGE_SIZE 4096 +#define IOCOST_COST_F_MERGE (1ULL << 0) /* not in BTF: a plain macro */ + +/* DIV64_U64_ROUND_UP / DIV_ROUND_UP_ULL equivalents, folded at + * compile time */ +#define RU(x, y) ((x) / (y) + (((x) % (y)) ? 1 : 0)) + +#define RBPS 174019176ULL +#define RSEQIOPS 41708ULL +#define RRANDIOPS 370ULL +#define WBPS 178075866ULL +#define WSEQIOPS 42705ULL +#define WRANDIOPS 378ULL + +#define RPAGE (RU(VTIME_PER_SEC, RU(RBPS, IOC_PAGE_SIZE))) +#define RSEQIO (RU(VTIME_PER_SEC, RSEQIOPS) - RPAGE) +#define RRANDIO (RU(VTIME_PER_SEC, RRANDIOPS) - RPAGE) +#define WPAGE (RU(VTIME_PER_SEC, RU(WBPS, IOC_PAGE_SIZE))) +#define WSEQIO (RU(VTIME_PER_SEC, WSEQIOPS) - WPAGE) +#define WRANDIO (RU(VTIME_PER_SEC, WRANDIOPS) - WPAGE) + +#define NSLOTS 4 + +struct streams { + __u64 expected[NSLOTS]; /* next expected sector, per stream */ + __u64 stamp[NSLOTS]; /* LRU stamp, 0 =3D empty */ +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u64); + __type(value, struct streams); + __uint(max_entries, 64); +} stream_tab SEC(".maps"); + +SEC("struct_ops") +u64 BPF_PROG(iocost_ms_calc_cost, u64 op, u64 nbytes, u64 sector, + u64 cursor, u64 iocg_id, u64 flags) +{ + struct streams *s; + u64 page, seqio, randio, base, advance, now; + u32 i, victim =3D 0, found =3D 0xFFFFFFFF; + + if (op !=3D REQ_OP_READ && op !=3D REQ_OP_WRITE) + return 0; /* delegate the rest to builtin */ + if (!nbytes) + return 0; + + if (op =3D=3D REQ_OP_READ) { + page =3D RPAGE; seqio =3D RSEQIO; randio =3D RRANDIO; + } else { + page =3D WPAGE; seqio =3D WSEQIO; randio =3D WRANDIO; + } + advance =3D RU(nbytes, 512); /* sectors */ + + s =3D bpf_map_lookup_elem(&stream_tab, &iocg_id); + if (!s) { + struct streams zero =3D {}; + + bpf_map_update_elem(&stream_tab, &iocg_id, &zero, + BPF_NOEXIST); + s =3D bpf_map_lookup_elem(&stream_tab, &iocg_id); + if (!s) + return seqio + RU(nbytes, IOC_PAGE_SIZE) * page; + } + + if (flags & IOCOST_COST_F_MERGE) { + base =3D 0; + goto out; + } + + now =3D bpf_ktime_get_ns(); + for (i =3D 0; i < NSLOTS; i++) { + if (s->expected[i] =3D=3D sector && s->stamp[i]) { + found =3D i; + break; + } + } + if (found !=3D 0xFFFFFFFF) { + base =3D seqio; + s->expected[found] =3D sector + advance; + s->stamp[found] =3D now; + } else { + base =3D randio; + for (i =3D 1; i < NSLOTS; i++) { + if (s->stamp[i] < s->stamp[victim]) + victim =3D i; + } + s->expected[victim] =3D sector + advance; + s->stamp[victim] =3D now; + } +out: + return base + RU(nbytes, IOC_PAGE_SIZE) * page; +} + +SEC(".struct_ops") +struct iocost_model_ops iocost_ms =3D { + .calc_cost =3D (void *)iocost_ms_calc_cost, +}; + +char LICENSE[] SEC("license") =3D "GPL"; --=20 2.43.0 From nobody Fri Sep 25 21:40:13 2026 Received: from mta1.migadu.com (out-45.mta1.migadu.com [95.215.58.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4272D5013CA for ; Tue, 8 Sep 2026 10:03:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861813; cv=none; b=pREUck6NV7PEEg8MkVpyS6opf0bvS9kPgDJH22tipM5I7W7jI2RPityMBQZzrWdipqUpyCbh2wqqmpArt3S/QoRFdyU+i1esKBoR94PqF9feHyx4a/YZTzTKI5coBSzcWHWIo/PPnUIEWPaxD2xS637+MOA/Mzy/yIWoUy6dJhU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788861813; c=relaxed/simple; bh=k5Y//fCuyAhbYOt7/YCpHnHqsrzwl/eKE/03WaIacHw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=un0BMaY1L1fXLLzPc4pBFpKz0cxKuRnw99V0B11SfLxUaeYAL5iqycKhf3GeJXop2r4Whmu5iZOhAu2J6NieQ71mP+nI2Z9YVgVr3S6dQ2dV7vwDbrpGG+T/fROXUV4otuUhABfeIEjdmrZ7uUP7uta4CAW+rQwDSM012GN2WIc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=DRyqWAQ/; arc=none smtp.client-ip=95.215.58.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="DRyqWAQ/" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=k5Y//fCuyAhbYOt7/YCpHnHqsrzwl/eKE/03WaIacHw=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1788861809; v=1; x=1789466609; b=DRyqWAQ/01uOxFLLVdU0Q3kcUtbK0o8j+ZZ1WjXK5DF86otKxmQUkRvVMLzifVzmt2JKbjjO ciXGVelUME4KVM030fOrQu3qbJf9d9gzURgAl79PxQ6Anq8nPoNcy/5EV9ly3+KCad116vNirWm 4V4jYvLi1f4MwIBvydRRrR/Y= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id d758f2e248c73fe8; Tue, 08 Sep 2026 10:03:29 +0000 X-Mizu-Trace-ID: d758f2e248c73fe8 X-Migadu-Flow: FLOW_OUT From: Tao Cui 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 Subject: [RFC PATCH 8/8] docs: cgroup-v2: document io.cost ctrl=bpf option Date: Tue, 8 Sep 2026 18:01:43 +0800 Message-ID: <20260908100143.47598-9-cui.tao@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260908100143.47598-1-cui.tao@linux.dev> References: <20260908100143.47598-1-cui.tao@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Tao Cui Document the "bpf" value of the ctrl key of io.cost.model and the iocost_model_ops struct_ops it selects, in the io.cost.model section of the cgroup v2 documentation: the registration precondition and single-instance limit, the scalar arguments, the vtime clamp and the delegation-to-builtin semantics of a zero return value. Signed-off-by: Tao Cui --- Documentation/admin-guide/cgroup-v2.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-= guide/cgroup-v2.rst index 8d2603751c51a..54b0574ce586c 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -2115,10 +2115,24 @@ IO Interface Files are defined. =20 =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - ctrl "auto" or "user" + ctrl "auto", "user" or "bpf" model The cost model in use - "linear" =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 + When "ctrl" is "bpf" (only available when + CONFIG_BLK_CGROUP_IOCOST_BPF is enabled), cost calculation for + the device is dispatched to the BPF program registered against + the iocost_model_ops struct_ops. Writing "bpf" fails with -EINVAL + until such a program is registered, and at most one program can + be registered system-wide. The model is called with scalar + arguments (operation, size, sector, cgroup cursor, css id of the + issuing cgroup, flags; see include/linux/blk-iocost.h for their + meaning) and returns a cost in vtime units, clamped by the kernel + to 1 second of device time per IO. A return value of 0 delegates + the IO back to the builtin linear formula, so a model which only + handles some IO types cannot make the rest free. Devices which + don't opt in keep the builtin linear model unchanged. + When "ctrl" is "auto", the kernel may change all parameters dynamically. When "ctrl" is set to "user" or any other parameters are written to, "ctrl" become "user" and the --=20 2.43.0