RK3588 kernel-6.1 fs/ext4/mballoc.c 多块分配器(mballoc)分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/mballoc.c(约 6700+ 行,ext4 数据块/簇 的主分配与释放实现)- 头文件与结构:
mballoc.h、ext4_allocation_request/ext4_allocation_context、ext4_group_info中的 buddy/prealloc 字段 - 调用方:
extents.c/indirect.c(ext4_mb_new_blocks)、inode.c(延迟分配、ext4_claim_free_clusters等)、ioctl.c(FITRIM→ext4_trim_fs)、fsmap.c(ext4_mballoc_query_range)
2. 代码作用(一句话)
mballoc.c 在 块组粒度 上维护 位图 + buddy 树(通过 s_buddy_cache 伪 inode 的页缓存 装载),按 goal、stripe、CR(cr 0/1) 与可选 mb_optimize_scan 选择块组;分配路径 优先吃 inode 预分配 / per-CPU 局部性预分配,否则 规范化请求长度 后在 buddy 上 查找、分裂、标记已用,并写回 块位图、gdt 空闲计数、簇计数(bigalloc 下以 簇 为 accounting 单位);释放路径 ext4_free_blocks 清位图、buddy、quota,并支持 日记 forget/revoke;另提供 TRIM(discard) 与 对空闲区间的枚举回调(getfsmap)。
3. 解决了什么问题
| 问题 | 处理方式 |
|---|---|
| 传统按块扫描位图,大块分配慢且碎片严重 | buddy:按 2 的幂阶 合并/分裂空闲区,快速找到 足够长的连续空闲簇 |
| 每分配一次就写位图,小文件随机写放大 | 预分配(PA):inode 级 i_prealloc_list(按 逻辑块 覆盖)、per-CPU s_locality_groups(按 剩余空闲 消费);未用完的规范化余量 挂回 PA 列表 |
| 小文件也希望物理相邻 | 小请求(低于 s_mb_stream_request,可 sysfs 调)倾向 组级预分配,使小文件 同组聚集 |
| 大块/顺序写希望一次拿长连续区 | ext4_mb_normalize_request:按 文件大小启发、mb_group_prealloc、stripe 等 放大申请长度,多拿的进 PA |
| 多 CPU 争用同一 free list | locality group 按 CPU 分桶;buddy 页在组锁 ext4_lock_group 下更新 |
| buddy 与位图占用内存 | s_buddy_cache:按需 ext4_mb_load_buddy,位图+buddy 两 block/组 映射到 页缓存;卸载丢弃 |
| 选组从 O(N) 扫描 | 可选 mb_optimize_scan:s_mb_largest_free_orders(最大空闲阶链表)、s_mb_avg_fragment_size(按 bb_free/bb_fragments 分桶),注释称 CR0/CR1 阶段近似 O(1) 选组 |
| RAID stripe 对齐 | s_stripe、ext4_mb_scan_aligned 等,尝试 与条带对齐的连续分配 |
| 配额与全局空闲簇 | ext4_claim_free_clusters / dquot_alloc_block(ext4_mb_new_blocks 开头);失败时 缩短 len 重试 或 -EDQUOT/-ENOSPC |
| 延迟分配已预留簇 | EXT4_MB_DELALLOC_RESERVED:跳过 claim/quot 中的 “必须有可用块” 分支,与 s_dirtyclusters_counter 配合 |
| fast commit 重放 | ext4_mb_new_blocks_simple / ext4_free_blocks_simple:简化路径 |
| 释放时集群边界 | ext4_free_blocks:EXT4_PBLK_COFF / EXT4_LBLK_COFF 处理 首尾部分簇;EXT4_FREE_BLOCKS_NOFREE_* 控制是否动边界簇 |
| 释放块合法性 | ext4_inode_block_valid(除非 VALIDATED);防 释放数据区外块 |
| FITRIM / SSD discard | ext4_trim_fs:按 fstrim_range 遍历块组,ext4_trim_all_free 对 够长的空闲簇 sb_issue_discard(受 granularity、minlen 约束) |
| getfsmap 需要枚举组内空闲 | ext4_mballoc_query_range:在 [start,end] 簇 内对 0 bit 段 调 formatter(fsmap.c 使用) |
| 在线加组后 buddy 初始化 | ext4_mb_init_group、ext4_mb_add_groupinfo 等与 resize 协作 |
4. 主路径概要
4.1 分配:ext4_mb_new_blocks
- TRACE / FC_REPLAY → simple。
- 非 delalloc reserved:
ext4_claim_free_clusters+dquot_alloc_block(配额文件可用 nofail)。 ext4_mb_initialize_context:填充ext4_allocation_context(含 goal、flags)。ext4_mb_use_preallocated:命中 inode PA 则直接ext4_mb_mark_diskspace_used。- 否则
ext4_mb_normalize_request→ext4_mb_pa_alloc(创建/填充上下文中的 PA)→ext4_mb_regular_allocator(ext4_mb_find_by_goal/ simple / complex / scan_aligned` 等)。 ext4_mb_mark_diskspace_used:日记 get_access、位图置位、buddy 更新、gdt、ext4_es_insert_extent由调用方负责。- 失败:
ext4_mb_pa_free/ext4_discard_allocated_blocks、释放配额预留。
4.2 释放:ext4_free_blocks
可选 ext4_forget(元数据或 data journal 语义)→ ext4_mb_clear_bb(清位图、更新 buddy、gdt、percpu_counter 等)。
5. 调试与 sysfs(文件头注释摘录)
可调参数包括 mb_stream_req、mb_group_prealloc、mb_min_to_scan、mb_max_to_scan、mb_order2_req、mb_linear_limit 等与 stripe、mb_optimize_scan 挂载选项;/sys/fs/ext4/<dev>/ 下暴露。
6. 小结
mballoc.c 是 ext4 数据区空间管理 的核心:在块组内用 buddy 做连续空闲管理,在文件系统级用 PA 做吞吐与局部性;与 bigalloc(簇)、quota、journal、FC 重放、discard、fsmap 深度绑定。阅读 ext4_map_blocks → ext4_mb_new_blocks 时,应以 ext4_allocation_request 各字段(goal/logical/len/flags) 为线索;阅读 碎片与延迟分配 时结合 extents_status.c 与 inode.c delalloc。
深层算法细节(mb_mark_used 分裂 buddy、ext4_mb_good_group_nolock、prefetch 等)可在本文件内按符号继续下钻,本文不展开到函数级伪代码。
正在加载留言…