存储-3-IO调度-2-相关文档(bfq-iosched.rst/blkio-controller.rst)


一、bfq-iosched.rst 翻译

==========================
BFQ(Budget Fair Queueing)
==========================

BFQ 是一个按比例共享(proportional-share)的 I/O 调度器,并带有额外的低时延能力。除了支持 cgroups(blkio 或 io 控制器)之外,BFQ 的主要特性包括:

- BFQ 能保证较高的系统与应用响应性,并为时延敏感应用(如音视频播放器)提供低时延;
- BFQ 按“带宽”而非仅按“时间片”在进程或组之间分配资源(在需要维持高吞吐时会回退到按时间分配)。

在默认配置下,BFQ 优先保证时延而非吞吐。因此,当为了降低时延有必要时,BFQ 可能构造出吞吐略低的调度序列。如果你在某个设备上的唯一目标是始终获得最大吞吐,应关闭全部低时延启发式:将 low_latency 设为 0。如何在时延与吞吐间取舍、或如何最大化吞吐,见第 3 节。

与其他 I/O 调度器一样,BFQ 会给每个 I/O 请求处理增加一定开销。为便于量化,这里给出一个示例:
BFQ 在单锁保护下的每请求总处理时间(即插入、派发、完成三个 hook 的执行时间之和)约为 1.9 us(Intel Core i7-2760QM@2.40GHz,使用简单代码插桩和 S 套件 [1] 的 throughput-sync.sh)。
作为对比,blk-mq 中更轻量的 mq-deadline 约为 0.7 us(mq-deadline 代码量约 800 行,BFQ 约 10500 行)。

调度开销会进一步限制 CPU 可处理的最大 IOPS(I/O 栈其他部分本身也会限制上限)。在启用完整层级支持(CONFIG_BFQ_GROUP_IOSCHED=y)且未开启 CONFIG_BFQ_CGROUP_DEBUG(见 4-2)时,BFQ 在下列 CPU 上的上限示例为:

- Intel i7-4850HQ:400 KIOPS
- AMD A8-3850:250 KIOPS
- ARM CortexTM-A53 八核:80 KIOPS

若开启 CONFIG_BFQ_CGROUP_DEBUG(且完整层级支持开启),由于会创建并更新全部 blkio.bfq* 统计项(见 4-2),可持续吞吐会下降。对应上面三台机器,最大可持续吞吐约为:

- Intel i7-4850HQ:310 KIOPS
- AMD A8-3850:200 KIOPS
- ARM CortexTM-A53 八核:56 KIOPS

BFQ 同样适用于多队列设备。

.. 下方为目录,可直接跳到第 3 节。

.. CONTENTS

1. 何时 BFQ 会有价值?
1-1 个人系统
1-2 服务器系统
2. BFQ 如何工作?
3. BFQ 的可调参数及正确配置方法
4. BFQ 组调度
4-1 提供的服务保证
4-2 接口

1. 何时 BFQ 会有价值?

BFQ 在个人系统和服务器系统上可带来以下收益。

1-1 个人系统
--------------------

交互式应用低时延
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

无论后台负载如何,BFQ 都能保证交互任务体验接近“设备空闲时”的响应性。比如即使后台正在执行:

- 读取、写入或复制一个或多个大文件;
- 编译源码树;
- 一个或多个虚拟机进行 I/O;
- 软件更新;
- 索引服务扫描文件系统并更新数据库;

此时启动应用、或在应用内加载文件,耗时都接近设备空闲时。对比 CFQ、NOOP、DEADLINE:在同等条件下应用会出现高时延,甚至在后台任务结束前卡顿(SSD 上同样可能出现)。

软实时应用低时延
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

音视频播放器/推流器等软实时应用,也能在后台 I/O 干扰下保持低时延与低丢包(丢帧)率,因此几乎不会因后台负载而出现明显卡顿。

代码开发类任务更快
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

若并行存在额外负载,BFQ 对典型开发任务(编译、checkout、merge 等)中的 I/O 相关阶段,
通常比 CFQ、NOOP、DEADLINE 更快。

高吞吐
^^^^^^^^^^^^^^^

在机械硬盘上,BFQ 在测试中的顺序负载吞吐可比 CFQ 高至 30%,比 DEADLINE/NOOP 高至 150%。在随机负载及闪存设备负载上,BFQ 吞吐通常与其他调度器相近。

强公平性、带宽与时延保证
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

BFQ 会按权重在 I/O 密集应用之间分配“设备吞吐”而非仅设备时间,且与负载模式、设备参数无关。基于这些带宽保证,可用简单公式推导出较紧的单请求时延保证。若未配置为严格服务保证模式,对于可能导致吞吐损失的应用,BFQ 会(仅)回退到基于时间的共享策略。

1-2 服务器系统
------------------

服务器收益大多来自上述同一服务属性。特别是,即使同时承载其他重负载,BFQ 仍可保证:

* 音视频流播放/推流的抖动和丢包率极低或接近 0;
* Web 页面及嵌入对象的快速获取;
* 在线数据采集/转储场景(例如抓包日志)的实时写入;
* 本地与远程访问服务器的响应性。

 

2. BFQ 如何工作

BFQ 是一个按比例共享 I/O 调度器,其总体结构及部分代码来源于 CFQ。

(1) 每个在设备上发起 I/O 的进程都会关联一个权重和一个 `(bfq_)queue`。

(2) BFQ 在一段时间内对某个队列(进程)授予设备独占访问,并通过为每个队列分配 “预算(budget)”来实现该模型,预算单位为扇区数。

(2.1) 队列获得设备访问后,每次派发请求,队列预算按请求大小递减。

(2.2) 在服役队列会在以下事件之一发生时过期(服务被挂起):1)预算耗尽,2)队列变空,3)“预算超时”触发。

- 预算超时用于防止随机 I/O 进程长时间占用设备并显著降低吞吐。

- 实际上,与 CFQ 类似,发同步请求的进程所对应队列在变空时不一定立即过期。BFQ 可能短暂 idle 设备,等待该进程及时发来新请求后继续服务。设备 idle 往往能提升旋转介质和非队列型闪存设备在同步顺序 I/O 场景下的吞吐。此外,在 BFQ 中,idle 也是保证同步请求进程获得目标吞吐份额的关键手段(详见本文 slice_idle 参数说明,或 [1, 2])。

- 从“服务保证”角度看,如果多个进程同时竞争设备且所有进程/组权重相同,BFQ 可在“完全不 idle 设备”的情况下仍保证预期吞吐分配,因此吞吐尽可能高。

- 对带内部命令队列的闪存设备(典型是 NCQ),idle 通常总会损害吞吐。因而 BFQ 仅在“服务保证必须”时执行 idle(例如为保证低时延或公平性)。此时总体吞吐可能次优。目前在这类设备上尚无同时满足“强服务保证”与“最优吞吐”的通用方案。

(2.3) 若开启低时延模式(默认开启),BFQ 会执行启发式检测交互应用与软实时应用 (如音视频播放器/推流器),并降低其时延。最关键动作是给这些应用对应队列分配超过公平份额的吞吐。为简洁起见,把这整套优待动作称为“提权(weight-raising)”。其中交互应用提权较温和,软实时应用提权更强。

(2.4) 对在“队列创建突发”中出现的队列,BFQ 会自动停用 idle。这些队列通常来自更偏吞吐的应用或服务进程,例如启动阶段的 systemd 或 git grep。

(2.5) 与 CFQ 一样,BFQ 会合并交错 I/O 队列(单看各自是随机 I/O,合并后趋于顺序)。但 BFQ 使用更快响应机制 EQM(Early Queue Merge)。EQM 能更早检测交错 I/O(协作进程),使 BFQ 即便在 CFQ 需要“抢占”机制才能保持高吞吐的场景中,也能通过队列合并保持高吞吐。因此 EQM 统一了交错 I/O 场景下的高吞吐手段。

(2.6) 队列按 WF2Q+ 的变体 B-WF2Q+ 调度,借助增强 rb-tree 实现 O(log N) 总体复杂度。详见 [2]。B-WF2Q+ 也天然支持层级调度,见第 4 节。

(2.7) B-WF2Q+ 相对理想、公平且平滑的服务模型,偏差很小。特别地,即使设备吞吐波动,且不受设备参数、当前负载、队列预算影响,B-WF2Q+ 仍保证每个队列按其权重比例获得设备吞吐份额。

(2.8) 最后一项“与预算大小无关”的性质(虽然乍看反直觉)实际上很有价值:

- 第一,任何按比例共享调度器相对理想服务的最大偏差都与最大预算(slice)成正比。因此 BFQ 的偏差能保持很小,不仅因为 B-WF2Q+ 本身精确,还因为 BFQ *不需要* 通过增大预算来让队列拿到更高吞吐份额。

- 第二,BFQ 可以为每个进程(队列)自由选择更匹配其需求或 I/O 模式的预算。BFQ 用简单的反馈回路算法动态更新预算,以在高吞吐与低时延保证之间取得平衡。当在服役队列过期时,该算法会计算其下一轮预算,以便:

1) 最终给顺序 I/O 的 I/O 密集型应用分配较大预算,因为单次连续服务越长,吞吐通常越高;

2) 最终给时延敏感应用(常见为间歇短 I/O)分配较小预算,因为等待服务队列预算越小,B-WF2Q+ 越快轮到它(见 [2] 的 3.3 小节)。

(3) 若多个进程同时竞争设备且进程/组权重一致,BFQ 可在不 idle 设备的情况下得到预期吞吐分配,此时会用抢占机制,且吞吐更高。

(4) ioprio class 间严格按优先级顺序服务:只要存在高优先级队列,低优先级队列不服务。同一 class 内按队列权重比例分配带宽。Idle class 仍会获得极薄的保底带宽,避免饿死。

 

3. BFQ 的可调参数及正确配置方法

BFQ 的大多数调参项会影响服务保证(主要是时延、公平性)与吞吐。要在两者之间取舍,重点看 slice_idle、strict_guarantees、low_latency;
要最大化吞吐,重点看 slice_idle、timeout_sync、max_budget。
其余性能相关参数多继承自 CFQ,主要为兼容保留。到目前为止,修改这些兼容参数在 BFQ 上很少带来明显性能提升。

特别是下文 back_seek-max、back_seek_penalty、fifo_expire_async、fifo_expire_sync 与 CFQ 基本一致,其说明沿用了 CFQ 描述。slice_idle 的部分说明也参考了 CFQ。

per-process ioprio and weight
-----------------------------

除非使用 cgroups 接口(见“4. BFQ 组调度”),否则进程权重只能通过 I/O 优先级间接设置,关系为:

weight = (IOPRIO_BE_NR - ioprio) * 10. //8, ioprio 越小 weight 越大。

注意:若 low_latency 打开,BFQ 会自动提高交互与软实时应用对应队列的权重。如果你需要精确控制权重,请关闭该项。


slice_idle
----------

该参数指定当某些同步 BFQ 队列变空时,BFQ 等待下一次 I/O 请求的 idle 时长。默认 slice_idle 非 0。idle 目的有二:提升吞吐、确保预期吞吐分配(见第 2 节)。

在寻道代价高的介质(如单盘 SATA/SAS 机械盘)上,idle 通常有利于减少寻道次数并提高吞吐。

将 slice_idle 设为 0 可移除队列 idle。在较快存储(如硬件 RAID 下多 SATA/SAS 盘,以及带内部命令队列/并行性的闪存设备)上,通常可提升整体吞吐。

因此,是否设为 0 取决于设备和负载。一般而言:

- SATA/SAS 单盘及其软件 RAID,保持 slice_idle 打开通常更有利;
- 单 LUN 背后有多主轴(主机侧硬 RAID 或阵列)或快速闪存存储,slice_idle=0 可能带来更高吞吐且时延仍可接受。

但在“权重差异”或“请求长度差异”场景下,idle 是实现服务保证所必需。举例:若队列 A 相比队列 B 需要连续派发多个请求,idle 能确保 A 在刚变空后很快补发新请求时,不会被 B 的请求插入打断,从而保持 A 在 B 下次派发前继续拿到多个派发机会。
请注意,这里保证的是“派发顺序层面”的差异化;若要保证实际服务顺序也严格对应派发顺序,还需设置 strict_guarantees。

idle 也有明显副作用:在不利于吞吐的场景下(典型是随机负载)会显著降低吞吐。因此 BFQ 在“对吞吐无益”时会尽量避免 idle(见第 2 节)。由此,加上 strict_guarantees 相关的附加因素,短期服务保证可能偶尔被打破。但某些场景中这些短期保证比最大吞吐更重要,例如视频播放/推流中低丢帧率往往比峰值吞吐更重要。此时可考虑设置 strict_guarantees。


slice_idle_us
-------------

与 slice_idle 控制同一参数,但单位是微秒。二者任意一个都可设置 idle 行为;设置后,另一个 sysfs 参数会反映新值。


strict_guarantees
-----------------

若设置该参数(默认未设置),则 BFQ 会:

- 在服役队列变空时始终执行 idle;

- 强制设备一次只服务一个 I/O 请求:只有不存在未完成请求时才派发新请求。

在存在权重差异或请求大小差异时,上述两条都需要,才能保证每个 BFQ 队列拿到应得带宽份额。第一条原因见 slice_idle 说明;第二条是因为现代存储设备会在内部重排排队请求,这可能直接破坏调度器在派发层面建立的服务保证。

设置 strict_guarantees 会影响吞吐,这是预期行为。


back_seek_max
-------------

单位 Kbytes,指定“向后寻道”的最大距离。该距离是从当前磁头位置到后向扇区的空间跨度。

该参数允许调度器提前考虑“后向”请求:若其距离在阈值内,可将其视为“下一个”请求。


back_seek_penalty
-----------------

用于计算向后寻道代价。若后向请求距离仅为前向请求的 1/back_seek_penalty,则二者寻道代价视为等价。

此时调度器不会偏向某一请求(否则会偏向前向请求)。默认值为 2。


fifo_expire_async
-----------------

异步请求超时时间。默认 248ms。


fifo_expire_sync
----------------

同步请求超时时间。默认 124ms。若希望相对偏向同步请求,应把该值设置得低于 fifo_expire_async。


low_latency
-----------

启用/禁用 BFQ 低时延模式。默认启用。启用后,交互与软实时应用会被优待并获得更低时延,机制见第 2 节。

如果你需要完全控制带宽分配,请关闭该模式。因为开启后 BFQ 会自动提升被优待应用的带宽份额。

此外,正如文档开头所述:如果你唯一目标是高吞吐,也应关闭该模式。因为优待部分应用的 I/O 可能降低总吞吐。对非旋转设备,要拿到尽可能高吞吐,可能还需将 slice_idle 设为 0(代价是放弃强公平/低时延保证)。


timeout_sync
------------

队列一旦被选中进入服务后,可连续占用设备的最长时间。在寻道代价高的设备上,增大该值通常有助于提高峰值吞吐。但同时会降低短期带宽与时延保证的粒度,尤其是下一个参数为 0 时更明显。


max_budget
----------

单个 BFQ 队列进入服务后可获得的最大服务量,单位扇区(当然仍受 timeout_sync 限制)。根据算法特性,该值越大,在顺序 I/O 占比越高时吞吐越高;代价是短期带宽/时延保证粒度变粗。

默认值为 0,表示自动调优:BFQ 会根据估计峰值速率,把 max_budget 设置为 timeout_sync 时间内可服务的最大扇区数。

针对特定设备,有用户反馈显式设置 max_budget(设置为 >0,且高于自动调优值)可获得更高吞吐。另一种达到相近效果的方法是:保持 max_budget=0,只提高 timeout_sync。


weights
-------

只读参数,用于展示当前活跃 BFQ 队列的权重。注: 5.4内核上已没有这个文件了。

 

4. BFQ 组调度

BFQ 同时支持 cgroups-v1 与 cgroups-v2 的 io 控制器,即 blkio 与 io。BFQ 支持基于权重的按比例共享。要激活 cgroups 支持,设置 CONFIG_BFQ_GROUP_IOSCHED

4-1 提供的服务保证
-------------------------------

在 BFQ 下,按比例共享表示“设备带宽”的真实按比例分配,而不是仅时间按比例。例如权重 200 的组可获得约两倍于权重 100 组的带宽。

BFQ 支持任意深度层级(组树)。带宽分配符合直觉:对每个组,其子节点按权重比例共享该组总带宽。特别地,这意味着:对于每个叶子组,除非修改进程 ioprio,该组内各进程获得的“整个组带宽份额”相同。

若严格按带宽保证会导致吞吐损失过大,组的资源共享保证可能部分或全部从“带宽”切换为“时间”。该切换按“每进程”进行:若叶子组中某进程以其带宽份额服务会引起明显吞吐下降,BFQ 会仅对该进程回退到按时间比例共享。

4-2 接口
-------------

若要在某设备上使用 BFQ 的按比例带宽共享,前提当然是该设备的活动调度器就是 BFQ。

在每个组目录下,BFQ 专属 cgroup 参数与统计文件都以 "bfq." 为前缀。因此在 cgroups-v1 或 cgroups-v2 中,完整前缀分别是 "blkio.bfq." 与 "io.bfq."。例如,设置组权重的参数文件是 blkio.bfq.weight 或 io.bfq.weight。

对于 cgroups-v1(blkio 控制器),bfq 创建并持续更新哪些统计文件,取决于 CONFIG_BFQ_CGROUP_DEBUG 是否开启:

- 若开启,则会创建 Documentation/admin-guide/cgroup-v1/blkio-controller.rst 中文档化的全部统计文件;
- 若未开启,则仅创建:
blkio.bfq.io_service_bytes
blkio.bfq.io_service_bytes_recursive
blkio.bfq.io_serviced
blkio.bfq.io_serviced_recursive

CONFIG_BFQ_CGROUP_DEBUG 的取值(y/n)对 BFQ 的可持续最大吞吐影响很大,因为更新 blkio.bfq.* 统计项本身代价较高(其中部分统计在开启 DEBUG 时才启用)。

/dev/blkio/background # ls -l
-r--r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.io_service_bytes
-r--r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.io_service_bytes_recursive
-r--r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.io_serviced
-r--r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.io_serviced_recursive
-rw-r--r-- 1 root   root   0 1970-01-01 08:00 blkio.bfq.weight
-rw-r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.weight_device
--w------- 1 root   root   0 2026-06-26 17:49 blkio.reset_stats
-rw-r--r-- 1 root   root   0 2026-06-26 17:49 cgroup.clone_children
-rw-rw-r-- 1 system system 0 1970-01-01 08:00 cgroup.procs
-rw-r--r-- 1 root   root   0 2026-06-26 17:49 notify_on_release
-rw-rw-r-- 1 system system 0 2026-06-25 15:58 tasks

可设置参数
-----------------

每个组可设置的参数只有下列一项:

weight(即 blkio.bfq.weight 或 io.bfq.weight):组在父组内的权重。取值范围 1..10000(默认 100)。
本节开头给出的 ioprio 与 weight 的线性映射仍然有效,但超过 IOPRIO_BE_NR*10 的权重,会映射为 ioprio 0。

再次提醒:若 low_latency 已设置,BFQ 会自动提升交互与软实时应用队列的权重。如果你希望自行控制权重,应关闭该选项。


[1]
P. Valente, A. Avanzini, "Evolution of the BFQ Storage I/O Scheduler", Proceedings of the First Workshop on Mobile System Technologies (MST-2015), May 2015.

http://algogroup.unimore.it/people/paolo/disk_sched/mst-2015.pdf

[2]
P. Valente and M. Andreolini, "Improving Application Responsiveness with the BFQ Disk I/O Scheduler", Proceedings of the 5th Annual International Systems and Storage Conference
(SYSTOR '12), June 2012.

Slightly extended version: http://algogroup.unimore.it/people/paolo/disk_sched/bfq-v1-suite-results.pdf

[3]
https://github.com/Algodev-github/S

 

补充

1. 对交互型和软实时应用的识别

交互型主要看“长时间 idle 后重新活跃”,软实时主要看“周期性、低带宽、受速率上限约束”的 I/O 模式。

不是靠 Android framework 给 BFQ 打标签,也不是识别“前台 Activity / 音视频 App 名字”。
BFQ 识别的是队列的 I/O 行为模式,不是应用语义。 也就是说,它在块层看一个 bfq_queue 的请求到达节奏、空闲时间、吞吐速率、是否 seeky、是否处于大 burst 中,然后把这个队列“推断”为 interactive 或 soft real-time。

同一个 App 可能对应多个 I/O 队列,BFQ 并不知道“这个包名是音乐播放器”,它只根据 I/O 模式推断“这个队列像不像交互型/软实时型”。

1.1 interactive 的识别:

条件很直接:不在 large burst 里, 这个队列之前 idle 了足够长时间, 重新活跃后,会被认为值得 weight-raising, 真正的 interactive 判定代码是:

idle_for_long_time = bfq_bfqq_idle_for_long_time(...)
*interactive = !in_burst && idle_for_long_time

见 bfq-iosched.c 和 bfq-iosched.c。

而 “idle 够久” 的定义在 bfq-iosched.c, 当前没有正在派发中的请求:bfqq->dispatched == 0, 当前时间已经晚于:budget_timeout + bfq_wr_min_idle_time。也就是说,一个队列如果空闲了一段最小时间,之后又重新发起 I/O,BFQ 会把它看作可能是交互型队列。
默认这个最小 idle 时间是 2 秒,定义在 bfq-iosched.c:bfqd->bfq_wr_min_idle_time = msecs_to_jiffies(2000);

所以可以粗暴理解为:长时间没动,突然来一小波同步 I/O,不像后台批处理的大 burst,BFQ 就倾向于认为这是交互型。这和桌面/手机上“用户点一下应用,触发一次短促 I/O”这种模式是吻合的。

1.2 soft real-time 的识别

soft real-time 不是靠包名,而是靠I/O 节奏像流媒体/音视频那样稳定、低带宽、成批次、批次之间有处理间隔。

直接判定条件在 bfq-iosched.c:
bfq_wr_max_softrt_rate > 0
!BFQQ_TOTALLY_SEEKY(bfqq)
!in_burst
time_is_before_jiffies(bfqq->soft_rt_next_start)
bfqq->dispatched == 0

拆开说:不能是 totally seeky 的随机乱跳队列, 不能处于大 burst, 必须满足一个“下一次请求到来时间”的下界约束, 当前不能还有未完成派发请求, 最关键的是 soft_rt_next_start,它的设计目的在代码注释里解释得非常详细,见 bfq-iosched.c。

代码说明了 soft real-time 需要满足两类特征:
(1) 平均带宽不能太高;
(2) 请求模式要接近 isochronous。意思是:发一批请求, 等这批请求完成, 停一会儿做数据处理, 再发下一批.这很像音视频播放、录制、推流。

soft_rt_next_start 的计算公式在 bfq-iosched.c:取三者最大值(旧的 soft_rt_next_start, 基于“这批已服务数据量 / 允许的软实时最大速率”算出来的下次最早启动时间, 当前时刻 + slice_idle + 额外 4 jiffies)

所以 BFQ 的意思是:如果你下一批请求来得太快,说明你更像 greedy / I/O-bound 队列,不像软实时。如果你有处理间隔,而且平均带宽不高,才更像 soft real-time。

soft real-time 的带宽阈值在 bfq-iosched.c:bfqd->bfq_wr_max_softrt_rate = 7000,后面的注释写得很直白:这是“播放或录制压缩高清录像所需的大致速率”。也就是说,BFQ 默认把“类似压缩高清视频播放/录制的速率级别”当作 soft real-time 的参考上限。

1.3 优待逻辑

一旦判定为 interactive 或 soft real-time,就进入 weight-raising 路径。相关逻辑在 bfq-iosched.c 和 bfq-iosched.c。默认 interactive 的权重提升系数来自 bfq-iosched.c:bfqd->bfq_wr_coeff = 30

也就是被认定为该类队列后,BFQ 会把它的调度权重明显放大,从而让它更快拿到设备服务。

1.4 小结

识别 interactive: 像不像“长时间安静,偶尔被用户触发,然后来一阵短促同步 I/O”的队列;
识别 soft real-time: 像不像“低带宽、周期性、一批一批发、批次之间有处理停顿”的队列;

 

2. 小结

bfq 支持 blkio cgroup带宽控制,支持基于 ioprio 的IO优先级。


二、blkio-controller.rst

===================
块 I/O 控制器
===================

概述
========
cgroup 子系统 "blkio" 实现了块 I/O 控制器。存储层级中,叶子节点与中间节点都存在多种 I/O 控制策略需求(例如按比例分配带宽、最大带宽限制)。
设计目标是:blkio 控制器统一采用同一套基于 cgroup 的管理接口,并根据用户选项在后台切换具体 I/O 策略。

其中一种 I/O 控制策略是限速(throttling)策略,可用于指定设备 I/O 速率上限。该策略实现于通用块层,不仅可用于叶子节点,也可用于如 device mapper 这类上层逻辑设备。

1. 如何使用

限速/上限策略
-----------------------------
- 启用块 I/O 控制器::

CONFIG_BLK_CGROUP=y

- 在块层启用限速::

CONFIG_BLK_DEV_THROTTLING=y

- 挂载 blkio 控制器(见 cgroups.txt 中 “Why are cgroups needed?”)::

mount -t cgroup -o blkio none /sys/fs/cgroup/blkio

- 为 root 组在某个设备上设置带宽限制。策略格式为 "<major>:<minor> <bytes_per_second>"::

    echo "8:16  1048576" > /sys/fs/cgroup/blkio/blkio.throttle.read_bps_device

上述命令会把主次设备号为 8:16 的设备在 root 组上的读速率限制为 1MB/s。

- 运行 dd 读取文件,观察速率是否被限制到 1MB/s::

    # dd iflag=direct if=/mnt/common/zerofile of=/dev/null bs=4K count=1024
    1024+0 records in
    1024+0 records out
    4194304 bytes (4.2 MB) copied, 4.0001 s, 1.0 MB/s

写入上限可通过 blkio.throttle.write_bps_device 设置。


2. 层级 cgroups

throttling 实现了层级支持;不过,只有在 cgroup 侧启用 "sane_behavior" 时,该层级支持才会生效。而 "sane_behavior" 目前是开发选项,尚未正式对外提供。

如果有人创建如下层级::

            root
            /  \
        test1 test2
            |
            test3

在开启 "sane_behavior" 时,throttling 会正确处理该层级。对 throttling 而言:所有 limit 作用于整棵子树;而统计仅针对该 cgroup 内任务直接产生的 I/O(本地统计)。

若 cgroup 侧未开启 "sane_behavior",throttling 实际上会把所有组当成同一层,效果近似如下::

                 pivot
            /  /    \     \
        root  test1 test2  test3


3. 用户可见的配置项

CONFIG_BLK_CGROUP - 块 I/O 控制器。
CONFIG_BFQ_CGROUP_DEBUG - 调试辅助。当前若开启该选项,cgroup 下会出现一些额外统计文件。
CONFIG_BLK_DEV_THROTTLING - 在块层启用块设备限速支持。


4. cgroup 文件详解

/dev/blkio/background # ls -l
-r--r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.io_service_bytes
-r--r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.io_service_bytes_recursive
-r--r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.io_serviced
-r--r--r-- 1 root   root   0 2026-06-26 17:49 blkio.bfq.io_serviced_recursive
-rw-r--r-- 1 root   root   0 1970-01-01 08:00 blkio.bfq.weight
-rw-r--r-- 1 root   root   0 2026-06-26 18:25 blkio.bfq.weight_device
--w------- 1 root   root   0 2026-06-26 17:49 blkio.reset_stats
-rw-r--r-- 1 root   root   0 2026-06-26 17:49 cgroup.clone_children
-rw-rw-r-- 1 system system 0 1970-01-01 08:00 cgroup.procs
-rw-r--r-- 1 root   root   0 2026-06-26 17:49 notify_on_release
-rw-rw-r-- 1 system system 0 2026-06-25 15:58 tasks

按比例权重策略文件
--------------------------------

- blkio.weight

指定每个 cgroup 的权重。这是该组在所有设备上的默认权重,除非被按设备规则覆盖(见 blkio.weight_device)。当前允许范围为 10 到 1000。


- blkio.weight_device

可通过该接口为“每 cgroup、每设备”设置规则。这些规则会覆盖 blkio.weight 指定的组默认权重。

格式如下::

# echo dev_maj:dev_minor weight > blkio.weight_device

在此 cgroup 中给 /dev/sdb (8:16) 配置 weight=300::

# echo 8:16 300 > blkio.weight_device
# cat blkio.weight_device
dev     weight
8:16    300

在此 cgroup 中给 /dev/sda (8:0) 配置 weight=500::

# echo 8:0 500 > blkio.weight_device
# cat blkio.weight_device
dev     weight
8:0     500
8:16    300

删除此 cgroup 中 /dev/sda 的专属权重::

# echo 8:0 0 > blkio.weight_device
# cat blkio.weight_device
dev     weight
8:16    300

注:主次设备号对应的设备使用 bfq 调度类才能echo成功。cat /proc/partitions 看主次设备号,echo bfq > /sys/block/X/queue/scheduler 配置为 bfq 调度类。


- blkio.time

该 cgroup 在各设备上分配到的磁盘时间(毫秒)。前两列为设备主次号,第三列为分配给该组的磁盘时间(毫秒)。


- blkio.sectors

该组向磁盘读写的扇区数。前两列为设备主次号,第三列为该组在该设备上的传输扇区数。


- blkio.io_service_bytes

该组与磁盘之间传输的字节数。进一步按操作类型细分:read/write、sync/async。前两列为设备主次号,第三列为操作类型,第四列为字节数。


- blkio.io_serviced

该组向磁盘发出的 I/O(bio)数量。进一步按操作类型细分:read/write、sync/async。前两列为设备主次号,第三列为操作类型,第四列为 I/O 数量。


- blkio.io_service_time

该 cgroup 的 I/O 从请求派发到请求完成的总时间。单位纳秒(便于在闪存设备上也有意义)。当 queue depth=1 时,该时间可视为实际服务时间;当 queue_depth > 1 时则不再成立,因为请求可能乱序完成。这会导致某个 I/O 的服务时间中包含其他 I/O 的服务时间,从而出现 total io_service_time > 实际流逝时间。
同样按 read/write、sync/async 细分。前两列为设备主次号,第三列为操作类型,第四列为 io_service_time(ns)。


- blkio.io_wait_time

该 cgroup 的 I/O 在调度器队列中等待服务的总时间。由于是所有 I/O 的累计等待时间,该值可大于总流逝时间。它不是“该 cgroup 总等待时长”,而是各 I/O 等待时长之和。对 queue_depth > 1 的设备,该指标不包含 I/O 被派发到设备后、到实际被服务前的等待时间(设备重排请求会带来这段时延)。单位纳秒(便于闪存设备统计)。
按 read/write、sync/async 细分。前两列为设备主次号,第三列为操作类型,第四列为 io_wait_time(ns)。


- blkio.io_merged

合并进本 cgroup 请求中的 bio/request 总数。按 read/write、sync/async 细分。


- blkio.io_queued

在任意时刻该 cgroup 排队中的请求总数。按 read/write、sync/async 细分。


- blkio.avg_queue_size

仅在 CONFIG_BFQ_CGROUP_DEBUG=y 时提供,属调试辅助。表示该 cgroup 从创建以来的平均队列长度。每当该 cgroup 某个队列获得一次 timeslice 时采样。


- blkio.group_wait_time

仅在 CONFIG_BFQ_CGROUP_DEBUG=y 时提供,属调试辅助。表示 cgroup 自变忙(即排队请求数从 0 到 1)后,到其某个队列获得 timeslice 之间所等待的时间。这与 io_wait_time 不同:io_wait_time 是该组内每个 I/O 在调度队列中的等待时长累计值。单位纳秒。
若在 cgroup 正处于等待 timeslice 状态时读取,统计只包含到上次获得 timeslice 为止的累计值,不含当前这段增量。


- blkio.empty_time

仅在 CONFIG_BFQ_CGROUP_DEBUG=y 时提供,属调试辅助。表示 cgroup 在“未被服务且没有挂起请求”状态下持续的时间。不包含 cgroup 某个队列被 idle 等待的时间。单位纳秒。若在 cgroup 处于 empty 状态时读取,统计只包含到上次存在挂起请求为止的累计值,不含当前这段增量。


- blkio.idle_time

仅在 CONFIG_BFQ_CGROUP_DEBUG=y 时提供,属调试辅助。表示 I/O 调度器为该 cgroup idle 等待、以期等到比其他队列 /cgroup 现有请求更优请求的时间。单位纳秒。若在 cgroup 处于 idling 状态时读取,统计只包含到上次 idle 周期为止的累计值,不含当前这段增量。


- blkio.dequeue

仅在 CONFIG_BFQ_CGROUP_DEBUG=y 时提供,属调试辅助。给出该组从设备服务树中被 dequeue 的次数。前两列为设备主次号,第三列为该组在该设备上的 dequeue 次数。


- blkio.*_recursive

各类统计的递归版本。这些文件展示的信息与非递归文件相同,但会包含所有子孙 cgroup 的统计。


限速/上限策略文件
-----------------------------------

- blkio.throttle.read_bps_device

指定设备读速率上限,单位 bytes/s。规则按设备生效。格式如下::

echo "<major>:<minor>  <rate_bytes_per_second>" > /cgrp/blkio.throttle.read_bps_device


- blkio.throttle.write_bps_device

指定设备写速率上限,单位 bytes/s。规则按设备生效。格式如下::

echo "<major>:<minor> <rate_bytes_per_second>" > /cgrp/blkio.throttle.write_bps_device


- blkio.throttle.read_iops_device

指定设备读 IOPS 上限,单位 IO/s。规则按设备生效。格式如下::

echo "<major>:<minor> <rate_io_per_second>" > /cgrp/blkio.throttle.read_iops_device


- blkio.throttle.write_iops_device

指定设备写 IOPS 上限,单位 IO/s。规则按设备生效。格式如下::

echo "<major>:<minor>  <rate_io_per_second>" > /cgrp/blkio.throttle.write_iops_device

注意:若同一设备同时配置了带宽(BW)和 IOPS 规则,则 I/O 会同时受两种约束。


- blkio.throttle.io_serviced

该组向磁盘发出的 I/O(bio)数量。进一步按操作类型细分:read/write、sync/async。前两列为设备主次号,第三列为操作类型,第四列为 I/O 数量。


- blkio.throttle.io_service_bytes

该组与磁盘之间传输的字节数。进一步按操作类型细分:read/write、sync/async。前两列为设备主次号,第三列为操作类型,第四列为字节数。


各策略共用文件
-----------------------------------


- blkio.reset_stats

向该文件写入一个整数会重置该 cgroup 的全部统计数据。

 

三、Kconfig.iosched

注: 翻译自 block/Kconfig.iosched

if BLOCK

menu "IO Schedulers"

config MQ_IOSCHED_DEADLINE
    tristate "MQ deadline I/O scheduler"
    default y
    ---help---
    deadline IO 调度器的 MQ(多队列)版本。

config MQ_IOSCHED_KYBER
    tristate "Kyber I/O scheduler"
    default y
    ---help---
    Kyber I/O 调度器是一个低开销调度器,适用于多队列以及其他高速设备。给定读请求和同步写请求的目标时延后,它会自动调优队列深度以实现该目标。

config IOSCHED_BFQ
    tristate "BFQ I/O scheduler"
    ---help---
    面向 BLK-MQ 的 BFQ I/O 调度器。BFQ 会按照各进程权重在所有进程之间分配设备带宽,且不受设备参数和负载类型影响。它还可为交互式和软实时应用提供低时延保障。
    详情见 Documentation/block/bfq-iosched.rst

config BFQ_GROUP_IOSCHED
    bool "BFQ hierarchical scheduling support"
    depends on IOSCHED_BFQ && BLK_CGROUP
    ---help---
    启用 BFQ 的层级调度能力,使用 blkio(cgroups-v1)或 io(cgroups-v2)控制器。

config BFQ_CGROUP_DEBUG
    bool "BFQ IO controller debugging"
    depends on BFQ_GROUP_IOSCHED
    ---help---
    启用一些调试辅助功能。目前会在 cgroup 中导出额外的统计文件,便于调试。

endmenu

endif

 

posted on 2026-06-26 18:00  Hello-World3  阅读(9)  评论(0)    收藏  举报

导航