BPF List
 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, eddyz87@gmail.com, ast@kernel.org,
	daniel@iogearbox.net, linux-kselftest@vger.kernel.org,
	cui.tao@linux.dev, cuitao@kylinos.cn, ameryhung@gmail.com,
	alexei.starovoitov@gmail.com
Subject: [PATCH 1/5] blk-iocost: move shared definitions to blk-iocost.h
Date: Wed, 28 Oct 2026 22:35:30 +0800	[thread overview]
Message-ID: <20261028143534.135968-2-cui.tao@linux.dev> (raw)
In-Reply-To: <20261028143534.135968-1-cui.tao@linux.dev>

From: Tao Cui <cuitao@kylinos.cn>

The BPF cost model code to be added in the following patch needs the
ioc/ioc_gq definitions and the queue accessors which are private to
blk-iocost.c.  Move the enums, the structs and the accessors into a
private blk-iocost.h so that everything can go where it belongs.
blkcg_policy_iocost becomes an extern declaration in the header so
that the inline accessors can be used from both translation units.

No functional change intended.

Suggested-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
 MAINTAINERS        |   1 +
 block/blk-iocost.c | 415 +-----------------------------------------
 block/blk-iocost.h | 436 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 439 insertions(+), 413 deletions(-)
 create mode 100644 block/blk-iocost.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 365bec8eb7fb..b3b421ee262e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6827,6 +6827,7 @@ F:	Documentation/admin-guide/cgroup-v1/blkio-controller.rst
 F:	block/bfq-cgroup.c
 F:	block/blk-cgroup.c
 F:	block/blk-iocost.c
+F:	block/blk-iocost.h
 F:	block/blk-iolatency.c
 F:	block/blk-throttle.c
 F:	include/linux/blk-cgroup.h
diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index a81488c755ca..9b610734871f 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -184,6 +184,7 @@
 #include "blk-stat.h"
 #include "blk-wbt.h"
 #include "blk-cgroup.h"
+#include "blk-iocost.h"
 
 #ifdef CONFIG_TRACEPOINTS
 
@@ -209,374 +210,6 @@ static char trace_iocg_path[TRACE_IOCG_PATH_LEN];
 #define TRACE_IOCG_PATH(type, iocg, ...)	do { } while (0)
 #endif	/* CONFIG_TRACE_POINTS */
 
-enum {
-	MILLION			= 1000000,
-
-	/* timer period is calculated from latency requirements, bound it */
-	MIN_PERIOD		= USEC_PER_MSEC,
-	MAX_PERIOD		= USEC_PER_SEC,
-
-	/*
-	 * iocg->vtime is targeted at 50% behind the device vtime, which
-	 * serves as its IO credit buffer.  Surplus weight adjustment is
-	 * immediately canceled if the vtime margin runs below 10%.
-	 */
-	MARGIN_MIN_PCT		= 10,
-	MARGIN_LOW_PCT		= 20,
-	MARGIN_TARGET_PCT	= 50,
-
-	INUSE_ADJ_STEP_PCT	= 25,
-
-	/* Have some play in timer operations */
-	TIMER_SLACK_PCT		= 1,
-
-	/* 1/64k is granular enough and can easily be handled w/ u32 */
-	WEIGHT_ONE		= 1 << 16,
-};
-
-enum {
-	/*
-	 * As vtime is used to calculate the cost of each IO, it needs to
-	 * be fairly high precision.  For example, it should be able to
-	 * represent the cost of a single page worth of discard with
-	 * suffificient accuracy.  At the same time, it should be able to
-	 * represent reasonably long enough durations to be useful and
-	 * convenient during operation.
-	 *
-	 * 1s worth of vtime is 2^37.  This gives us both sub-nanosecond
-	 * granularity and days of wrap-around time even at extreme vrates.
-	 */
-	VTIME_PER_SEC_SHIFT	= 37,
-	VTIME_PER_SEC		= 1LLU << VTIME_PER_SEC_SHIFT,
-	VTIME_PER_USEC		= VTIME_PER_SEC / USEC_PER_SEC,
-	VTIME_PER_NSEC		= VTIME_PER_SEC / NSEC_PER_SEC,
-
-	/* bound vrate adjustments within two orders of magnitude */
-	VRATE_MIN_PPM		= 10000,	/* 1% */
-	VRATE_MAX_PPM		= 100000000,	/* 10000% */
-
-	VRATE_MIN		= VTIME_PER_USEC * VRATE_MIN_PPM / MILLION,
-	VRATE_CLAMP_ADJ_PCT	= 4,
-
-	/* switch iff the conditions are met for longer than this */
-	AUTOP_CYCLE_NSEC	= 10LLU * NSEC_PER_SEC,
-};
-
-enum {
-	/* if IOs end up waiting for requests, issue less */
-	RQ_WAIT_BUSY_PCT	= 5,
-
-	/* unbusy hysterisis */
-	UNBUSY_THR_PCT		= 75,
-
-	/*
-	 * The effect of delay is indirect and non-linear and a huge amount of
-	 * future debt can accumulate abruptly while unthrottled. Linearly scale
-	 * up delay as debt is going up and then let it decay exponentially.
-	 * This gives us quick ramp ups while delay is accumulating and long
-	 * tails which can help reducing the frequency of debt explosions on
-	 * unthrottle. The parameters are experimentally determined.
-	 *
-	 * The delay mechanism provides adequate protection and behavior in many
-	 * cases. However, this is far from ideal and falls shorts on both
-	 * fronts. The debtors are often throttled too harshly costing a
-	 * significant level of fairness and possibly total work while the
-	 * protection against their impacts on the system can be choppy and
-	 * unreliable.
-	 *
-	 * The shortcoming primarily stems from the fact that, unlike for page
-	 * cache, the kernel doesn't have well-defined back-pressure propagation
-	 * mechanism and policies for anonymous memory. Fully addressing this
-	 * issue will likely require substantial improvements in the area.
-	 */
-	MIN_DELAY_THR_PCT	= 500,
-	MAX_DELAY_THR_PCT	= 25000,
-	MIN_DELAY		= 250,
-	MAX_DELAY		= 250 * USEC_PER_MSEC,
-
-	/* halve debts if avg usage over 100ms is under 50% */
-	DFGV_USAGE_PCT		= 50,
-	DFGV_PERIOD		= 100 * USEC_PER_MSEC,
-
-	/* don't let cmds which take a very long time pin lagging for too long */
-	MAX_LAGGING_PERIODS	= 10,
-
-	/*
-	 * Count IO size in 4k pages.  The 12bit shift helps keeping
-	 * size-proportional components of cost calculation in closer
-	 * numbers of digits to per-IO cost components.
-	 */
-	IOC_PAGE_SHIFT		= 12,
-	IOC_PAGE_SIZE		= 1 << IOC_PAGE_SHIFT,
-	IOC_SECT_TO_PAGE_SHIFT	= IOC_PAGE_SHIFT - SECTOR_SHIFT,
-
-	/* if apart further than 16M, consider randio for linear model */
-	LCOEF_RANDIO_PAGES	= 4096,
-};
-
-enum ioc_running {
-	IOC_IDLE,
-	IOC_RUNNING,
-	IOC_STOP,
-};
-
-/* io.cost.qos controls including per-dev enable of the whole controller */
-enum {
-	QOS_ENABLE,
-	QOS_CTRL,
-	NR_QOS_CTRL_PARAMS,
-};
-
-/* io.cost.qos params */
-enum {
-	QOS_RPPM,
-	QOS_RLAT,
-	QOS_WPPM,
-	QOS_WLAT,
-	QOS_MIN,
-	QOS_MAX,
-	NR_QOS_PARAMS,
-};
-
-/* io.cost.model controls */
-enum {
-	COST_CTRL,
-	COST_MODEL,
-	NR_COST_CTRL_PARAMS,
-};
-
-/* builtin linear cost model coefficients */
-enum {
-	I_LCOEF_RBPS,
-	I_LCOEF_RSEQIOPS,
-	I_LCOEF_RRANDIOPS,
-	I_LCOEF_WBPS,
-	I_LCOEF_WSEQIOPS,
-	I_LCOEF_WRANDIOPS,
-	I_LCOEF_FLUSHIOPS,
-	NR_I_LCOEFS,
-};
-
-enum {
-	LCOEF_RPAGE,
-	LCOEF_RSEQIO,
-	LCOEF_RRANDIO,
-	LCOEF_WPAGE,
-	LCOEF_WSEQIO,
-	LCOEF_WRANDIO,
-	LCOEF_FLUSH,
-	NR_LCOEFS,
-};
-
-enum {
-	AUTOP_INVALID,
-	AUTOP_HDD,
-	AUTOP_SSD_QD1,
-	AUTOP_SSD_DFL,
-	AUTOP_SSD_FAST,
-};
-
-struct ioc_params {
-	u32				qos[NR_QOS_PARAMS];
-	u64				i_lcoefs[NR_I_LCOEFS];
-	u64				lcoefs[NR_LCOEFS];
-	u32				too_fast_vrate_pct;
-	u32				too_slow_vrate_pct;
-};
-
-struct ioc_margins {
-	s64				min;
-	s64				low;
-	s64				target;
-};
-
-struct ioc_missed {
-	local_t				nr_met;
-	local_t				nr_missed;
-	u32				last_met;
-	u32				last_missed;
-};
-
-struct ioc_pcpu_stat {
-	struct ioc_missed		missed[2];
-
-	local64_t			rq_wait_ns;
-	u64				last_rq_wait_ns;
-};
-
-/* per device */
-struct ioc {
-	struct rq_qos			rqos;
-
-	bool				enabled;
-
-	struct ioc_params		params;
-	struct ioc_margins		margins;
-	u32				period_us;
-	u32				timer_slack_ns;
-	u64				vrate_min;
-	u64				vrate_max;
-
-	spinlock_t			lock;
-	struct timer_list		timer;
-	struct list_head		active_iocgs;	/* active cgroups */
-	struct ioc_pcpu_stat __percpu	*pcpu_stat;
-
-	enum ioc_running		running;
-	atomic64_t			vtime_rate;
-	u64				vtime_base_rate;
-	s64				vtime_err;
-
-	seqcount_spinlock_t		period_seqcount;
-	u64				period_at;	/* wallclock starttime */
-	u64				period_at_vtime; /* vtime starttime */
-
-	atomic64_t			cur_period;	/* inc'd each period */
-	int				busy_level;	/* saturation history */
-
-	bool				weights_updated;
-	atomic_t			hweight_gen;	/* for lazy hweights */
-
-	/* debt forgivness */
-	u64				dfgv_period_at;
-	u64				dfgv_period_rem;
-	u64				dfgv_usage_us_sum;
-
-	u64				autop_too_fast_at;
-	u64				autop_too_slow_at;
-	int				autop_idx;
-	bool				user_qos_params:1;
-	bool				user_cost_model:1;
-};
-
-struct iocg_pcpu_stat {
-	local64_t			abs_vusage;
-};
-
-struct iocg_stat {
-	u64				usage_us;
-	u64				wait_us;
-	u64				indebt_us;
-	u64				indelay_us;
-};
-
-/* per device-cgroup pair */
-struct ioc_gq {
-	struct blkg_policy_data		pd;
-	struct ioc			*ioc;
-
-	/*
-	 * A iocg can get its weight from two sources - an explicit
-	 * per-device-cgroup configuration or the default weight of the
-	 * cgroup.  `cfg_weight` is the explicit per-device-cgroup
-	 * configuration.  `weight` is the effective considering both
-	 * sources.
-	 *
-	 * When an idle cgroup becomes active its `active` goes from 0 to
-	 * `weight`.  `inuse` is the surplus adjusted active weight.
-	 * `active` and `inuse` are used to calculate `hweight_active` and
-	 * `hweight_inuse`.
-	 *
-	 * `last_inuse` remembers `inuse` while an iocg is idle to persist
-	 * surplus adjustments.
-	 *
-	 * `inuse` may be adjusted dynamically during period. `saved_*` are used
-	 * to determine and track adjustments.
-	 */
-	u32				cfg_weight;
-	u32				weight;
-	u32				active;
-	u32				inuse;
-
-	u32				last_inuse;
-	s64				saved_margin;
-
-	sector_t			cursor;		/* to detect randio */
-
-	/*
-	 * `vtime` is this iocg's vtime cursor which progresses as IOs are
-	 * issued.  If lagging behind device vtime, the delta represents
-	 * the currently available IO budget.  If running ahead, the
-	 * overage.
-	 *
-	 * `vtime_done` is the same but progressed on completion rather
-	 * than issue.  The delta behind `vtime` represents the cost of
-	 * currently in-flight IOs.
-	 */
-	atomic64_t			vtime;
-	atomic64_t			done_vtime;
-	u64				abs_vdebt;
-
-	/* current delay in effect and when it started */
-	u64				delay;
-	u64				delay_at;
-
-	/*
-	 * The period this iocg was last active in.  Used for deactivation
-	 * and invalidating `vtime`.
-	 */
-	atomic64_t			active_period;
-	struct list_head		active_list;
-
-	/* see __propagate_weights() and current_hweight() for details */
-	u64				child_active_sum;
-	u64				child_inuse_sum;
-	u64				child_adjusted_sum;
-	int				hweight_gen;
-	u32				hweight_active;
-	u32				hweight_inuse;
-	u32				hweight_donating;
-	u32				hweight_after_donation;
-
-	struct list_head		walk_list;
-	struct list_head		surplus_list;
-
-	struct wait_queue_head		waitq;
-	struct hrtimer			waitq_timer;
-
-	/* timestamp at the latest activation */
-	u64				activated_at;
-
-	/* statistics */
-	struct iocg_pcpu_stat __percpu	*pcpu_stat;
-	struct iocg_stat		stat;
-	struct iocg_stat		last_stat;
-	u64				last_stat_abs_vusage;
-	u64				usage_delta_us;
-	u64				wait_since;
-	u64				indebt_since;
-	u64				indelay_since;
-
-	/* this iocg's depth in the hierarchy and ancestors including self */
-	int				level;
-	struct ioc_gq			*ancestors[];
-};
-
-/* per cgroup */
-struct ioc_cgrp {
-	struct blkcg_policy_data	cpd;
-	unsigned int			dfl_weight;
-};
-
-struct ioc_now {
-	u64				now_ns;
-	u64				now;
-	u64				vnow;
-};
-
-struct iocg_wait {
-	struct wait_queue_entry		wait;
-	struct bio			*bio;
-	u64				abs_cost;
-	bool				committed;
-};
-
-struct iocg_wake_ctx {
-	struct ioc_gq			*iocg;
-	u32				hw_inuse;
-	s64				vbudget;
-};
-
 static const struct ioc_params autop[] = {
 	[AUTOP_HDD] = {
 		.qos				= {
@@ -656,49 +289,6 @@ static const u32 vrate_adj_pct[] =
 	  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
 	  4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 16 };
 
-static struct blkcg_policy blkcg_policy_iocost;
-
-/* accessors and helpers */
-static struct ioc *rqos_to_ioc(struct rq_qos *rqos)
-{
-	return container_of(rqos, struct ioc, rqos);
-}
-
-static struct ioc *q_to_ioc(struct request_queue *q)
-{
-	return rqos_to_ioc(rq_qos_id(q, RQ_QOS_COST));
-}
-
-static const char __maybe_unused *ioc_name(struct ioc *ioc)
-{
-	struct gendisk *disk = ioc->rqos.disk;
-
-	if (!disk)
-		return "<unknown>";
-	return disk->disk_name;
-}
-
-static struct ioc_gq *pd_to_iocg(struct blkg_policy_data *pd)
-{
-	return pd ? container_of(pd, struct ioc_gq, pd) : NULL;
-}
-
-static struct ioc_gq *blkg_to_iocg(struct blkcg_gq *blkg)
-{
-	return pd_to_iocg(blkg_to_pd(blkg, &blkcg_policy_iocost));
-}
-
-static struct blkcg_gq *iocg_to_blkg(struct ioc_gq *iocg)
-{
-	return pd_to_blkg(&iocg->pd);
-}
-
-static struct ioc_cgrp *blkcg_to_iocc(struct blkcg *blkcg)
-{
-	return container_of(blkcg_to_cpd(blkcg, &blkcg_policy_iocost),
-			    struct ioc_cgrp, cpd);
-}
-
 /*
  * Scale @abs_cost to the inverse of @hw_inuse.  The lower the hierarchical
  * weight, the more expensive each IO.  Must round up.
@@ -3150,7 +2740,6 @@ static u64 ioc_weight_prfill(struct seq_file *sf, struct blkg_policy_data *pd,
 	return 0;
 }
 
-
 static int ioc_weight_show(struct seq_file *sf, void *v)
 {
 	struct blkcg *blkcg = css_to_blkcg(seq_css(sf));
@@ -3616,7 +3205,7 @@ static struct cftype ioc_files[] = {
 	{}
 };
 
-static struct blkcg_policy blkcg_policy_iocost = {
+struct blkcg_policy blkcg_policy_iocost = {
 	.dfl_cftypes	= ioc_files,
 	.cpd_alloc_fn	= ioc_cpd_alloc,
 	.cpd_free_fn	= ioc_cpd_free,
diff --git a/block/blk-iocost.h b/block/blk-iocost.h
new file mode 100644
index 000000000000..e4e517ef6a0f
--- /dev/null
+++ b/block/blk-iocost.h
@@ -0,0 +1,436 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * blk-iocost private definitions, shared between blk-iocost.c and
+ * blk-iocost-bpf.c. Not for direct inclusion by other files.
+ */
+#ifndef _BLK_IOCOST_H
+#define _BLK_IOCOST_H
+
+#include <linux/types.h>
+#include <linux/blk_types.h>
+#include <linux/blkdev.h>
+#include "blk-cgroup.h"
+#include <linux/blk-mq.h>
+#include <linux/timer.h>
+#include <linux/hrtimer.h>
+#include <linux/seqlock.h>
+#include <linux/atomic.h>
+#include <linux/minmax.h>
+#include <linux/time64.h>
+#include <linux/wait.h>
+#include <asm/local.h>
+#include <asm/local64.h>
+#include "blk-rq-qos.h"
+
+enum {
+	MILLION			= 1000000,
+
+	/* timer period is calculated from latency requirements, bound it */
+	MIN_PERIOD		= USEC_PER_MSEC,
+	MAX_PERIOD		= USEC_PER_SEC,
+
+	/*
+	 * iocg->vtime is targeted at 50% behind the device vtime, which
+	 * serves as its IO credit buffer.  Surplus weight adjustment is
+	 * immediately canceled if the vtime margin runs below 10%.
+	 */
+	MARGIN_MIN_PCT		= 10,
+	MARGIN_LOW_PCT		= 20,
+	MARGIN_TARGET_PCT	= 50,
+
+	INUSE_ADJ_STEP_PCT	= 25,
+
+	/* Have some play in timer operations */
+	TIMER_SLACK_PCT		= 1,
+
+	/* 1/64k is granular enough and can easily be handled w/ u32 */
+	WEIGHT_ONE		= 1 << 16,
+};
+
+enum {
+	/*
+	 * As vtime is used to calculate the cost of each IO, it needs to
+	 * be fairly high precision.  For example, it should be able to
+	 * represent the cost of a single page worth of discard with
+	 * suffificient accuracy.  At the same time, it should be able to
+	 * represent reasonably long enough durations to be useful and
+	 * convenient during operation.
+	 *
+	 * 1s worth of vtime is 2^37.  This gives us both sub-nanosecond
+	 * granularity and days of wrap-around time even at extreme vrates.
+	 */
+	VTIME_PER_SEC_SHIFT	= 37,
+	VTIME_PER_SEC		= 1LLU << VTIME_PER_SEC_SHIFT,
+	VTIME_PER_USEC		= VTIME_PER_SEC / USEC_PER_SEC,
+	VTIME_PER_NSEC		= VTIME_PER_SEC / NSEC_PER_SEC,
+
+	/* bound vrate adjustments within two orders of magnitude */
+	VRATE_MIN_PPM		= 10000,	/* 1% */
+	VRATE_MAX_PPM		= 100000000,	/* 10000% */
+
+	VRATE_MIN		= VTIME_PER_USEC * VRATE_MIN_PPM / MILLION,
+	VRATE_CLAMP_ADJ_PCT	= 4,
+
+	/* switch iff the conditions are met for longer than this */
+	AUTOP_CYCLE_NSEC	= 10LLU * NSEC_PER_SEC,
+};
+
+enum {
+	/* if IOs end up waiting for requests, issue less */
+	RQ_WAIT_BUSY_PCT	= 5,
+
+	/* unbusy hysterisis */
+	UNBUSY_THR_PCT		= 75,
+
+	/*
+	 * The effect of delay is indirect and non-linear and a huge amount of
+	 * future debt can accumulate abruptly while unthrottled. Linearly scale
+	 * up delay as debt is going up and then let it decay exponentially.
+	 * This gives us quick ramp ups while delay is accumulating and long
+	 * tails which can help reducing the frequency of debt explosions on
+	 * unthrottle. The parameters are experimentally determined.
+	 *
+	 * The delay mechanism provides adequate protection and behavior in many
+	 * cases. However, this is far from ideal and falls shorts on both
+	 * fronts. The debtors are often throttled too harshly costing a
+	 * significant level of fairness and possibly total work while the
+	 * protection against their impacts on the system can be choppy and
+	 * unreliable.
+	 *
+	 * The shortcoming primarily stems from the fact that, unlike for page
+	 * cache, the kernel doesn't have well-defined back-pressure propagation
+	 * mechanism and policies for anonymous memory. Fully addressing this
+	 * issue will likely require substantial improvements in the area.
+	 */
+	MIN_DELAY_THR_PCT	= 500,
+	MAX_DELAY_THR_PCT	= 25000,
+	MIN_DELAY		= 250,
+	MAX_DELAY		= 250 * USEC_PER_MSEC,
+
+	/* halve debts if avg usage over 100ms is under 50% */
+	DFGV_USAGE_PCT		= 50,
+	DFGV_PERIOD		= 100 * USEC_PER_MSEC,
+
+	/* don't let cmds which take a very long time pin lagging for too long */
+	MAX_LAGGING_PERIODS	= 10,
+
+	/*
+	 * Count IO size in 4k pages.  The 12bit shift helps keeping
+	 * size-proportional components of cost calculation in closer
+	 * numbers of digits to per-IO cost components.
+	 */
+	IOC_PAGE_SHIFT		= 12,
+	IOC_PAGE_SIZE		= 1 << IOC_PAGE_SHIFT,
+	IOC_SECT_TO_PAGE_SHIFT	= IOC_PAGE_SHIFT - SECTOR_SHIFT,
+
+	/* if apart further than 16M, consider randio for linear model */
+	LCOEF_RANDIO_PAGES	= 4096,
+};
+
+enum ioc_running {
+	IOC_IDLE,
+	IOC_RUNNING,
+	IOC_STOP,
+};
+
+/* io.cost.qos controls including per-dev enable of the whole controller */
+enum {
+	QOS_ENABLE,
+	QOS_CTRL,
+	NR_QOS_CTRL_PARAMS,
+};
+
+/* io.cost.qos params */
+enum {
+	QOS_RPPM,
+	QOS_RLAT,
+	QOS_WPPM,
+	QOS_WLAT,
+	QOS_MIN,
+	QOS_MAX,
+	NR_QOS_PARAMS,
+};
+
+/* io.cost.model controls */
+enum {
+	COST_CTRL,
+	COST_MODEL,
+	NR_COST_CTRL_PARAMS,
+};
+
+/* builtin linear cost model coefficients */
+enum {
+	I_LCOEF_RBPS,
+	I_LCOEF_RSEQIOPS,
+	I_LCOEF_RRANDIOPS,
+	I_LCOEF_WBPS,
+	I_LCOEF_WSEQIOPS,
+	I_LCOEF_WRANDIOPS,
+	I_LCOEF_FLUSHIOPS,
+	NR_I_LCOEFS,
+};
+
+enum {
+	LCOEF_RPAGE,
+	LCOEF_RSEQIO,
+	LCOEF_RRANDIO,
+	LCOEF_WPAGE,
+	LCOEF_WSEQIO,
+	LCOEF_WRANDIO,
+	LCOEF_FLUSH,
+	NR_LCOEFS,
+};
+
+enum {
+	AUTOP_INVALID,
+	AUTOP_HDD,
+	AUTOP_SSD_QD1,
+	AUTOP_SSD_DFL,
+	AUTOP_SSD_FAST,
+};
+
+struct ioc_params {
+	u32				qos[NR_QOS_PARAMS];
+	u64				i_lcoefs[NR_I_LCOEFS];
+	u64				lcoefs[NR_LCOEFS];
+	u32				too_fast_vrate_pct;
+	u32				too_slow_vrate_pct;
+};
+
+struct ioc_margins {
+	s64				min;
+	s64				low;
+	s64				target;
+};
+
+struct ioc_missed {
+	local_t				nr_met;
+	local_t				nr_missed;
+	u32				last_met;
+	u32				last_missed;
+};
+
+struct ioc_pcpu_stat {
+	struct ioc_missed		missed[2];
+
+	local64_t			rq_wait_ns;
+	u64				last_rq_wait_ns;
+};
+
+/* per device */
+struct ioc {
+	struct rq_qos			rqos;
+
+	bool				enabled;
+
+	struct ioc_params		params;
+	struct ioc_margins		margins;
+	u32				period_us;
+	u32				timer_slack_ns;
+	u64				vrate_min;
+	u64				vrate_max;
+
+	spinlock_t			lock;
+	struct timer_list		timer;
+	struct list_head		active_iocgs;	/* active cgroups */
+	struct ioc_pcpu_stat __percpu	*pcpu_stat;
+
+	enum ioc_running		running;
+	atomic64_t			vtime_rate;
+	u64				vtime_base_rate;
+	s64				vtime_err;
+
+	seqcount_spinlock_t		period_seqcount;
+	u64				period_at;	/* wallclock starttime */
+	u64				period_at_vtime; /* vtime starttime */
+
+	atomic64_t			cur_period;	/* inc'd each period */
+	int				busy_level;	/* saturation history */
+
+	bool				weights_updated;
+	atomic_t			hweight_gen;	/* for lazy hweights */
+
+	/* debt forgivness */
+	u64				dfgv_period_at;
+	u64				dfgv_period_rem;
+	u64				dfgv_usage_us_sum;
+
+	u64				autop_too_fast_at;
+	u64				autop_too_slow_at;
+	int				autop_idx;
+	bool				user_qos_params:1;
+	bool				user_cost_model:1;
+
+};
+
+struct iocg_pcpu_stat {
+	local64_t			abs_vusage;
+};
+
+struct iocg_stat {
+	u64				usage_us;
+	u64				wait_us;
+	u64				indebt_us;
+	u64				indelay_us;
+};
+
+/* per device-cgroup pair */
+struct ioc_gq {
+	struct blkg_policy_data		pd;
+	struct ioc			*ioc;
+
+	/*
+	 * A iocg can get its weight from two sources - an explicit
+	 * per-device-cgroup configuration or the default weight of the
+	 * cgroup.  `cfg_weight` is the explicit per-device-cgroup
+	 * configuration.  `weight` is the effective considering both
+	 * sources.
+	 *
+	 * When an idle cgroup becomes active its `active` goes from 0 to
+	 * `weight`.  `inuse` is the surplus adjusted active weight.
+	 * `active` and `inuse` are used to calculate `hweight_active` and
+	 * `hweight_inuse`.
+	 *
+	 * `last_inuse` remembers `inuse` while an iocg is idle to persist
+	 * surplus adjustments.
+	 *
+	 * `inuse` may be adjusted dynamically during period. `saved_*` are used
+	 * to determine and track adjustments.
+	 */
+	u32				cfg_weight;
+	u32				weight;
+	u32				active;
+	u32				inuse;
+
+	u32				last_inuse;
+	s64				saved_margin;
+
+	sector_t			cursor;		/* to detect randio */
+
+	/*
+	 * `vtime` is this iocg's vtime cursor which progresses as IOs are
+	 * issued.  If lagging behind device vtime, the delta represents
+	 * the currently available IO budget.  If running ahead, the
+	 * overage.
+	 *
+	 * `vtime_done` is the same but progressed on completion rather
+	 * than issue.  The delta behind `vtime` represents the cost of
+	 * currently in-flight IOs.
+	 */
+	atomic64_t			vtime;
+	atomic64_t			done_vtime;
+	u64				abs_vdebt;
+
+	/* current delay in effect and when it started */
+	u64				delay;
+	u64				delay_at;
+
+	/*
+	 * The period this iocg was last active in.  Used for deactivation
+	 * and invalidating `vtime`.
+	 */
+	atomic64_t			active_period;
+	struct list_head		active_list;
+
+	/* see __propagate_weights() and current_hweight() for details */
+	u64				child_active_sum;
+	u64				child_inuse_sum;
+	u64				child_adjusted_sum;
+	int				hweight_gen;
+	u32				hweight_active;
+	u32				hweight_inuse;
+	u32				hweight_donating;
+	u32				hweight_after_donation;
+
+	struct list_head		walk_list;
+	struct list_head		surplus_list;
+
+	struct wait_queue_head		waitq;
+	struct hrtimer			waitq_timer;
+
+	/* timestamp at the latest activation */
+	u64				activated_at;
+
+	/* statistics */
+	struct iocg_pcpu_stat __percpu	*pcpu_stat;
+	struct iocg_stat		stat;
+	struct iocg_stat		last_stat;
+	u64				last_stat_abs_vusage;
+	u64				usage_delta_us;
+	u64				wait_since;
+	u64				indebt_since;
+	u64				indelay_since;
+
+	/* this iocg's depth in the hierarchy and ancestors including self */
+	int				level;
+	struct ioc_gq			*ancestors[];
+};
+
+/* per cgroup */
+struct ioc_cgrp {
+	struct blkcg_policy_data	cpd;
+	unsigned int			dfl_weight;
+};
+
+struct ioc_now {
+	u64				now_ns;
+	u64				now;
+	u64				vnow;
+};
+
+struct iocg_wait {
+	struct wait_queue_entry		wait;
+	struct bio			*bio;
+	u64				abs_cost;
+	bool				committed;
+};
+
+struct iocg_wake_ctx {
+	struct ioc_gq			*iocg;
+	u32				hw_inuse;
+	s64				vbudget;
+};
+
+extern struct blkcg_policy blkcg_policy_iocost;
+
+static inline struct ioc *rqos_to_ioc(struct rq_qos *rqos)
+{
+	return container_of(rqos, struct ioc, rqos);
+}
+
+static inline struct ioc *q_to_ioc(struct request_queue *q)
+{
+	return rqos_to_ioc(rq_qos_id(q, RQ_QOS_COST));
+}
+
+static inline const char __maybe_unused *ioc_name(struct ioc *ioc)
+{
+	struct gendisk *disk = ioc->rqos.disk;
+
+	if (!disk)
+		return "<unknown>";
+	return disk->disk_name;
+}
+
+static inline struct ioc_gq *pd_to_iocg(struct blkg_policy_data *pd)
+{
+	return pd ? container_of(pd, struct ioc_gq, pd) : NULL;
+}
+
+static inline struct ioc_gq *blkg_to_iocg(struct blkcg_gq *blkg)
+{
+	return pd_to_iocg(blkg_to_pd(blkg, &blkcg_policy_iocost));
+}
+
+static inline struct blkcg_gq *iocg_to_blkg(struct ioc_gq *iocg)
+{
+	return pd_to_blkg(&iocg->pd);
+}
+
+static inline struct ioc_cgrp *blkcg_to_iocc(struct blkcg *blkcg)
+{
+	return container_of(blkcg_to_cpd(blkcg, &blkcg_policy_iocost),
+			    struct ioc_cgrp, cpd);
+}
+
+#endif	/* _BLK_IOCOST_H */
-- 
2.43.0


  reply	other threads:[~2026-10-28 14:35 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28 14:35 [RFC PATCH v11 0/5] blk-iocost: BPF struct_ops cost model Tao Cui
2026-10-28 14:35 ` Tao Cui [this message]
2026-10-28 20:09   ` [PATCH 1/5] blk-iocost: move shared definitions to blk-iocost.h Tejun Heo
2026-10-29 10:04     ` Tao Cui
2026-10-28 14:35 ` [PATCH 2/5] blk-iocost: add BPF struct_ops cost model support Tao Cui
2026-10-28 15:07   ` sashiko-bot
2026-10-28 15:33   ` bot+bpf-ci
2026-10-28 20:09   ` Tejun Heo
2026-10-29 10:11     ` Tao Cui
2026-10-28 14:35 ` [PATCH 3/5] selftests/bpf: add iocost cost model test Tao Cui
2026-10-28 14:57   ` sashiko-bot
2026-10-28 15:33   ` bot+bpf-ci
2026-10-28 20:09   ` Tejun Heo
2026-10-29 10:30     ` Tao Cui
2026-10-28 14:35 ` [PATCH 4/5] blk-iocost: add iocost_ioc_tick tracepoint for per-period device summary Tao Cui
2026-10-28 20:09   ` Tejun Heo
2026-10-29 10:31     ` Tao Cui
2026-10-28 14:35 ` [PATCH 5/5] docs: cgroup-v2: document the iocost BPF cost model attachment Tao Cui
2026-10-28 20:09   ` Tejun Heo
2026-10-29 10:33     ` Tao Cui
2026-10-28 15:58 ` [RFC PATCH v11 0/5] blk-iocost: BPF struct_ops cost model Alexei Starovoitov
2026-10-28 20:09   ` Tejun Heo
2026-10-29  9:55   ` Tao Cui
2026-10-28 20:09 ` Tejun Heo
2026-10-29  9:57   ` 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=20261028143534.135968-2-cui.tao@linux.dev \
    --to=cui.tao@linux.dev \
    --cc=alexei.starovoitov@gmail.com \
    --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=eddyz87@gmail.com \
    --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