首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

RK3588 存储部署与排障

RK3588 存储部署与排障

1. 后端选择

后端 常见文件系统
eMMC/SD ext4、F2FS
NVMe/SATA ext4、XFS、Btrfs
raw NAND UBI + UBIFS
只读固件 EROFS、SquashFS
容器 root OverlayFS
临时数据 tmpfs
NAS/集群 NFS、SMB、CephFS

2. 编解码场景

多路视频写入特点:

  • 大文件顺序写;
  • 环形删除;
  • fsync 策略;
  • 突发码率;
  • DMA/CMA 与 page cache 争内存;
  • eMMC 写放大和寿命。

建议预分配文件、批量 metadata、控制保留空间,并实测掉电恢复。

3. 页缓存和内存

大量 buffered write 会占用 page cache。关注:

1
2
3
grep -E 'MemAvailable|Cached|Dirty|Writeback|Slab' /proc/meminfo
cat /proc/pressure/memory
cat /proc/pressure/io

容器业务同时设置 memory.high/max 和合理 writeback,避免一个 workload 的脏页挤压全系统及 CMA。

4. 存储观测

1
2
3
4
5
6
7
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS
df -hT
df -i
cat /proc/diskstats
iostat -xz 1
cat /sys/block/mmcblk0/queue/scheduler
cat /sys/block/nvme0n1/queue/scheduler

设备名按实际平台替换。

5. 错误定位

只读 remount

检查 dmesg 中 filesystem error、I/O error、journal abort、控制器 timeout。不要直接 remount rw 后继续覆盖证据。

ENOSPC

同时检查:

  • block;
  • inode;
  • reserved space;
  • quota;
  • Btrfs global reserve/chunk;
  • F2FS overprovision/GC;
  • deleted-but-open files。

“空间未释放”

1
2
lsof +L1
findmnt

unlink 只删除目录项,open/mmap 引用结束后数据才回收。

高延迟

区分:

  • filesystem journal/checkpoint;
  • dirty throttling;
  • block queue;
  • device GC/thermal;
  • reclaim;
  • sync storm;
  • 网络 RTT。

6. Mount options

不要套用统一“性能参数”。评估:

  • noatime/lazytime
  • commit/checkpoint 周期;
  • discard 在线或定期 fstrim;
  • compression;
  • barrier/flush(通常不应关闭);
  • errors policy;
  • data mode;
  • Overlay copy-up;
  • NFS/SMB cache。

关闭 barrier 可能导致断电损坏,除非硬件提供并已验证等效持久保障。

7. 固件更新

推荐:

  • A/B 只读 system image;
  • dm-verity/fs-verity;
  • 原子切换 boot slot;
  • 数据分区独立;
  • schema version;
  • 断电注入测试;
  • 可回滚;
  • upper layer migration。

8. 性能测试

基准应覆盖:

  • 冷/热缓存;
  • 顺序/随机;
  • 小文件 metadata;
  • fsync p99;
  • ENOSPC 接近满盘;
  • 长时间 steady-state;
  • 高温降速;
  • 掉电后 replay/fsck 时间;
  • 与真实编码业务并发。

9. 配置核对

源码目录存在某文件系统不等于镜像启用。使用:

1
2
zcat /proc/config.gz | grep -E 'EXT4|F2FS|EROFS|OVERLAY|UBIFS|NFS|CIFS|CEPH'
cat /proc/filesystems

模块还需安装到 rootfs,并满足 crypto、NLS、network 等依赖。

可执行文件加载与 Coredump

可执行文件加载与 Coredump

1. Exec 位于 VFS

exec.c 管理从 pathname/fd 加载新程序:

1
2
3
4
5
6
7
execve/execveat
-> do_execveat_common()
-> alloc_bprm()
-> bprm_execve()
-> prepare_binprm()
-> search_binary_handler()
-> linux_binfmt->load_binary()

成功 exec 替换当前进程地址空间、cred 和执行映像,而不是创建新进程。

2. linux_binprm

保存:

  • executable file;
  • filename/interpreter;
  • argv/env;
  • initial header buffer;
  • credentials;
  • stack limits;
  • recursion/interpreter state。

VFS permission、noexec mount、LSM、setuid/setgid/capability 都在加载阶段参与。

3. Binary format

linux_binfmt 通过 register_binfmt() 注册:

  • binfmt_elf.c
  • binfmt_script.c
  • binfmt_misc.c
  • binfmt_flat.c
  • ELF FDPIC(按配置)

search_binary_handler() 依次尝试匹配 handler。

4. ELF

ELF loader:

  • 校验 ELF header/program headers;
  • 打开 PT_INTERP 动态链接器;
  • 创建新 mm;
  • 映射 PT_LOAD;
  • 设置 brk、stack、auxv;
  • 处理 PIE/ASLR;
  • 最终 start_thread() 进入用户入口。

真实页通常在后续 page fault 时按需读入。

5. Script

#!interpreter optional-argbinfmt_script 解析,重写 argv 并让解释器成为下一执行文件。内核限制 shebang 长度/递归;用户输入路径仍需安全处理。

6. binfmt_misc

允许管理员按 magic/extension 注册用户态解释器,常用于 QEMU user emulation。配置接口本身具有高权限和代码执行风险。

7. Exec 并发

多线程 exec 需要:

  • 停止其他线程;
  • unshare files/sighand 等状态;
  • 关闭 CLOEXEC fd;
  • 处理 ptrace;
  • commit 新 cred;
  • 销毁旧 mm;
  • 保证失败发生在不可逆 point 前。

8. Coredump

coredump.c

1
2
3
4
5
6
fatal signal
-> do_coredump()
-> check dumpability/rlimit/pattern
-> open pipe/file target
-> binfmt->core_dump()
-> ELF core notes + VMAs/data

core_pattern 可指定文件名模板或 pipe helper。pipe helper 运行在高权限上下文,必须防命令注入和资源耗尽。

9. 安全与隐私

core 可能含密钥、用户数据和进程内存:

  • 配置 RLIMIT_CORE
  • 控制 dumpable;
  • 限制 core 文件权限和目录;
  • 容器考虑 namespace;
  • secretmem 等映射不应被普通 dump;
  • 上传前脱敏。

10. RK3588

嵌入式产品需在可诊断性和 flash 寿命间平衡。大型媒体进程 core 可能数 GiB,建议限额、压缩/流式收集、预留分区并防止写满系统盘。

RK3588 kernel-6.1 fs/ext4/acl.c POSIX ACL 支持分析

RK3588 kernel-6.1 fs/ext4/acl.c POSIX ACL 支持分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/acl.c
  • rk3588/kernel-6.1/fs/ext4/acl.h
  • xattr.c 中扩展属性接口、VFS posix_acl 通用层的衔接

编译依赖:CONFIG_EXT4_FS_POSIX_ACL(在 fs/ext4/Makefile 中把 acl.o 链入 ext4 模块)。


2. 代码作用(一句话)

acl.c 在 ext4 里把 POSIX ACL 落到磁盘:以“固定二进制布局”存放在扩展属性(xattr)中,并在读/写时与内核内存结构 struct posix_acl 互转;设置 ACL 时走 JBD2 事务 + ext4_xattr_set,新建文件时从父目录继承 default ACL。


3. 解决了什么问题

3.1 UNIX 经典权限的局限

传统 rwx 只对 owner / group / other 三组生效。需要下面能力时不够用:

  • 多个用户多个组 单独授权;
  • 目录上设 默认 ACL,使新建文件/子目录 自动继承 权限语义;
  • mask 一起限制 ACL 条目的有效权限(POSIX.1e 模型)。

POSIX ACL 在 Linux 上通过 setfacl/getfacl 等接口暴露;文件系统必须能 持久化 ACL 并在 permission 检查时参与决策。ext4 选择把它存在 xattr 里(与 ext3 时期思路一致),acl.c 负责 格式转换与 I/O 路径,实际权限检查由 VFS 通用 posix_acl_* + inode i_acl / i_default_acl 缓存完成。

3.2 ext4 侧具体承担的工作

问题 acl.c 的做法
盘上长什么样 ext4_acl_header + ext4_acl_entry / ext4_acl_entry_short,版本号 EXT4_ACL_VERSION
与用户态/通用 ACL 如何对应 ext4_acl_from_disk / ext4_acl_to_disk
从哪里读写 通过 ext4_xattr_get / ext4_xattr_set_handle,索引 EXT4_XATTR_INDEX_POSIX_ACL_ACCESS..._DEFAULT
一致性 ext4_set_aclext4_journal_start/stop,与元数据日志一致
access ACL 与 mode 位一致性 posix_acl_update_mode:访问 ACL 可能要求同步更新 i_mode 中 owner 类权限
新 inode ext4_init_acl:从父目录 posix_acl_create 得到 default + access,再写入子 inode

4. 磁盘格式(acl.h

  • ext4_acl_header:仅 a_version(当前 0x0001),小端。
  • ext4_acl_entrye_tage_perm、可选 e_id(用于 ACL_USER / ACL_GROUP 条目)。
  • ext4_acl_entry_short:无 e_id,用于 ACL_USER_OBJACL_GROUP_OBJACL_MASKACL_OTHER

条目数量与总长用 ext4_acl_count() / ext4_acl_size() 推算(前 4 条按 short 布局,多出的为带 id 的 full entry),与 acl.c 中遍历逻辑一致,防止畸形 xattr 越界解析。


5. 核心函数说明

5.1 ext4_acl_from_disk / ext4_acl_to_disk

  • 从盘读出:校验长度、a_versionext4_acl_count,逐条解析 e_tag/e_perm,对 ACL_USER/ACL_GROUPmake_kuid/make_kgidinit_user_ns)填入内存 ACL。
  • 写回盘:分配连续 buffer,e_tag/e_perm/e_id 转小端;from_kuid/from_kgid 写 id(同样在 init_user_ns 下序列化,与磁盘历史格式一致)。

解析结束指针必须 严格等于 end,否则 EINVAL,避免尾部垃圾或截断。

5.2 ext4_get_acl

VFS get_posix_acl 的 ext4 实现入口(由 ext4.h 里 inode 操作表挂接)。

  • 类型ACL_TYPE_ACCESS → access xattr;ACL_TYPE_DEFAULT → default xattr(仅目录语义在设置层限制)。
  • rcu == true:直接返回 ERR_PTR(-ECHILD),不支持在 RCU 下读 ACL(需一致快照则走非 RCU 路径)。
  • 通过 ext4_xattr_get 取 blob,再 ext4_acl_from_disk-ENODATA/-ENOSYS 视为 无 ACL(NULL)

5.3 __ext4_set_acl

内部函数:在已有 handle_text4_xattr_set_handle 写入或删除 xattr;成功后 set_cached_acl 更新 inode 上 ACL 缓存。

  • default ACL:若 inode 不是目录 且要设置非空 default ACL,返回 -EACCES(符合 POSIX:default ACL 只属于目录)。
  • acl == NULL:删除对应 xattr(size == 0)。

5.4 ext4_set_acl

对外设置入口:配额初始化 → 计算 xattr 所需 journal creditsext4_journal_start

  • ACL_TYPE_ACCESSacl 非空:调用 posix_acl_update_mode,必要时更新 inode->i_modei_ctime,并 ext4_mark_inode_dirty
  • -ENOSPC 时按 ext4 惯例 ext4_should_retry_alloc 重试。

5.5 ext4_init_acl

ext4_new_inode 路径调用(父目录 dir 已持锁;新 inode 仍独占)。

  • posix_acl_create(dir, &inode->i_mode, &default_acl, &acl):由通用层根据父目录 default ACL 和 umask 算出子节点的 default + access。
  • 分别 __ext4_set_acl(..., XATTR_CREATE) 写入;释放临时 ACL 引用;错误时仍要 release,避免泄漏。

6. 配置关闭时(acl.h

若未定义 CONFIG_EXT4_FS_POSIX_ACL

  • ext4_get_acl / ext4_set_acl 被定义为 NULL(不需 POSIX ACL 钩子)。
  • ext4_init_acl 内联为仅 inode->i_mode &= ~current_umask(),并注释说明:否则不会因未调用 posix_acl_create 而漏掉 umask。

7. 与其它模块的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
VFS / generic_acl / permission()
|
v
ext4_get_acl / ext4_set_acl(acl.c)
|
v
ext4_xattr_get / ext4_xattr_set_handle(xattr.c)
|
v
磁盘 inode 扩展属性块(或 inline xattr)
|
v
ext4_jbd2(事务提交)

8. RK3588 / 嵌入式注意点

  • 开启 CONFIG_EXT4_FS_POSIX_ACL 会增加代码体积与少量元数据路径开销;Android/服务器镜像若使用 ACL,需要打开;纯嵌入式只读 rootfs 可酌情关闭。
  • ACL 存 xattr:小文件 + 多 ACL 可能增加元数据与 fsck 负担;权限模型简单时继续用传统 chmod 即可。
  • user namespace 与挂载 idmap 场景下,盘上 uid/gid 与 init_user_ns 序列化方式需与上层策略一致;本文件 ACL 序列化固定走 init_user_ns,与多数 xattr ACL 实现一致。

9. 小结

维度 结论
作用 ext4 的 POSIX ACL:盘上 xattr 比特流 ↔ struct posix_acl,带日志的 set,新建 inode 继承
解决问题 在 ext4 上实现超出 user/group/other 的细粒度权限与目录默认 ACL,并与 VFS ACL 框架对接
关键依赖 xattr、JBD2、posix_acl_* 通用层、CONFIG_EXT4_FS_POSIX_ACL

文档对应路径:rk3588/kernel-6.1/fs/ext4/acl.c。保存位置:linuxDoc/fs/ext4/

RK3588 kernel-6.1 fs/ext4/balloc.c 块位图与块组元数据辅助分析

RK3588 kernel-6.1 fs/ext4/balloc.c 块位图与块组元数据辅助分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/balloc.c
  • mballoc.cext4_mb_new_blocks)、ext4.h 中块组宏、super.c 挂载初始化、ialloc.c 等资源统计的协作关系

2. 代码作用(一句话)

balloc.c 负责 ext4「块组 / 物理块号」相关的算术、块位图(block bitmap)的读入与校验/惰性初始化、每组元数据开销与「空闲簇」簿记(含预留与重试),并为元数据分配提供入口;现代 ext4 上「大块连续分配」本体在 mballoc.cballoc.c 是其底层与位图、组描述符之间的粘合层与策略辅助。

文件开头注释也写明:

1
* balloc.c contains the blocks allocation and deallocation routines

历史沿革上继承自 ext2/ext3;ext4 引入 mballoc 后,数据块 多由 mballoc 完成,但 位图管理、组布局、空闲计数与 ext4_new_meta_blocks 仍集中在本文件及相关调用链中。


3. 解决了什么问题

3.1 块号 ↔ 块组 + 位图偏移

ext4 按 块组 管理位图;给定全局 物理块号(或 cluster),必须可靠换算:

  • ext4_get_group_number:根据 STD_GROUP_SIZE 等挂载选项走快速路径或调用 ext4_get_group_no_and_offset
  • ext4_get_group_no_and_offset:减去 s_first_data_block 后对 EXT4_BLOCKS_PER_GROUP 取模,再按 s_cluster_bits 右移得到 簇级 位图下标(与 bigalloc 一致)。

解决的问题:统一块/簇与位图索引,避免分配与校验时用错组或错 bit。

3.2 哪些块根本不能用于数据分配(元数据开销)

  • ext4_num_base_meta_blocks / ext4_num_base_meta_clusters:超级块备份、GDT/备份、meta_bg 布局下每组 GDT 占用等。
  • ext4_num_overhead_clusters:在基础元数据之上,再把 块位图、inode 位图、inode table 所占簇计入(含非连续布局的特殊情况)。
  • ext4_free_clusters_after_init:对 未初始化位图 组,在位图不可用时不靠数bits,而用「组内簇数 − 开销」得到空闲簇数。

解决的问题:正确区分可分配数据区与位图/表项,用于挂载、统计与 lazy bitmap init 的初始内容生成。

3.3 块位图:读盘、校验、惰性初始化

  • ext4_init_block_bitmap:若组描述符 校验和失败,标记位图损坏并相当于 整组只读防分配;否则 memset 后对 基础元数据簇块/inode 位图、inode 表 对应 bit 置 1(已用),并对组尾 padding 置 1(ext4_mark_bitmap_end)。
  • ext4_read_block_bitmap_nowait / ext4_wait_block_bitmap / ext4_read_block_bitmap:拿 buffer_head、必要时提交读;若 EXT4_BG_BLOCK_UNINIT 且支持 checksumming,则在锁内 在内存中生成位图 并标 uptodate(无需先读盘)。
  • ext4_validate_block_bitmap:CRC(ext4_block_bitmap_csum_verify)、自洽性(块/inode 位图块号、inode 表区间在位图中须为已占用)、尾部 padding 必须为 1;FLEX_BG 下部分检查跳过(位图可能不在本组)。

解决的问题:检测损坏/篡改、支持 未初始化块组 减少 mkfs 后首次挂载 IO,并保证位图与组描述符描述一致。

3.4 组描述符与 per-group 运行时信息

  • ext4_get_group_desc:用 block_group 索引到 GDT 所在块 + 组内偏移,返回 struct ext4_group_desc *(挂载与 mballoc 大量使用)。
  • ext4_get_group_info:返回 ext4_group_info(mballoc 的 buddy 等 per-group 结构指针)。

解决的问题:从逻辑组号定位磁盘元数据,并把 balloc 与 mballoc 接到同一套组结构上。

3.5 全文件系统「是否还有空闲簇」与 ENOSPC 重试

  • ext4_has_free_clusters:用 s_freeclusters_counters_dirtyclusters_counters_resv_clustersr_blocks_count 等判断;低于 watermark 时做一次 percpu_counter 精确求和root 保留块CAP_SYS_RESOURCEEXT4_MB_USE_ROOT_BLOCKS / EXT4_MB_USE_RESERVED 有特殊规则。
  • ext4_claim_free_clusters:在认为有空间时把需求量加到 dirtyclusters_counter(与延迟分配、未落盘「已预订」语义配合)。
  • ext4_should_retry_alloc:分配返回 ENOSPC 时,若有 journal,可 jbd2_journal_force_commit_nested 等,等待事务落盘释放位图,最多重试 3 次;与 DISCARD work 等也有交互。

解决的问题:避免过早 ENOSPC(脏元数据尚未提交)、与 quota/预留块 策略一致,并给上层 ext4_set_acl、xattr、inode 分配-ENOSPC 重试 提供统一判据(见其他文件对 ext4_should_retry_alloc 的调用)。

3.6 元数据块分配入口

  • ext4_new_meta_blocks:填充 ext4_allocation_request 后调用 ext4_mb_new_blocks,用于 索引块、extent 树块等元数据;支持 EXT4_MB_DELALLOC_RESERVED 时的 quota 记账。

解决的问题:元数据分配与数据分配共用 mballoc,但 API 与标志集中在一处,便于与 handle 事务 对齐。

3.7 超级块 / GDT 备份布局

  • ext4_bg_has_supersparse_supersparse_super2、组 0/1 等规则决定本组是否有超级块备份。
  • ext4_bg_num_gdbmeta_bg 与传统布局下,本组内 组描述符表 占块数。

ext4_num_base_meta_blocks 一起支撑 resizefsck 语义理解位图初始化 时「哪些 bit 必须为 1」。

3.8 分配起点 hint:ext4_inode_to_goal_block

  • 使用 inode 所在块组 i_block_groupflex_bg 足够大时,目录等普通文件 分到 flex 内不同组以改善局部性与 fsck。
  • 未开 DELALLOC 时用 pid 上色colour)在同组内打散,减轻锁竞争;开 DELALLOC 时直接 组起始块 作 goal。

解决的问题:在 mballoc 决策前给出局部性友好的物理 hint,而非完全盲目搜索。

3.9 调试与一致性统计

  • ext4_count_free_clustersEXT4FS_DEBUG 下可对照组描述符累加值与位图逐位统计;非 DEBUG 则只累加组描述符中的空闲量(跳过已知损坏位图组)。

4. 与 mballoc.c 的分工(理解重点)

层面 balloc.c mballoc.c
位图缓冲区 读/校验/init 分配路径中大量使用已验证位图
实际找位、预分配、buddy 不承担 ext4_mb_new_blocks
元数据分配 API ext4_new_meta_blocks → mballoc 执行分配
组号/开销/GDT 核心 消费 ext4_get_group_descext4_get_group_info

因此:**「balloc」**一名更反映历史;当前应理解为 block bitmap & block group 基础设施,而不是「所有块都在此分配」。


5. 数据流示意(简化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
挂载 / 首次访问某组
|
v
ext4_read_block_bitmap* → ext4_validate_block_bitmap
|
+--> UNINIT: ext4_init_block_bitmap(内存生成)
|
v
mballoc 分配
|
v
ext4_has_free_clusters / ext4_claim_free_clusters
|
v
ENOSPC → ext4_should_retry_alloc(journal commit)

6. RK3588 / 工程注意点

  • 存储:eMMC/UFS 上 ext4 很常见;位图校验失败会 按组标记损坏,表现为 该组分区的分配失败或只读,需结合 dmesgfsck
  • 延迟分配 + ENOSPCext4_should_retry_allocjournal 相关,无 journal(极少见配置)时重试逻辑不同。
  • bigalloc:所有 「簇」 级计数与 EXT4_B2C 等宏与 balloc.c 一致,排查「统计不准」时要核对 s_cluster_bits

7. 关键导出符号/函数速查

1
2
3
4
5
6
7
8
9
10
11
ext4_get_group_number / ext4_get_group_no_and_offset
ext4_get_group_desc / ext4_get_group_info
ext4_read_block_bitmap / ext4_read_block_bitmap_nowait / ext4_wait_block_bitmap
ext4_free_clusters_after_init
ext4_has_free_clusters(static,对外经 ext4_claim_free_clusters)
ext4_claim_free_clusters
ext4_should_retry_alloc
ext4_new_meta_blocks
ext4_count_free_clusters
ext4_bg_has_super / ext4_bg_num_gdb / ext4_num_base_meta_blocks
ext4_inode_to_goal_block

8. 小结

维度 结论
作用 块组算术、块位图生命周期与校验、元数据开销与空闲簇簿记、元数据块分配入口、super/GDT 布局辅助、分配 goal
解决问题 在给定磁盘布局下 安全、惰性、可校验 地维护「哪一簇空闲」,并与 mballoc、journal、预留块 协同,避免错误分配与错误 ENOSPC

源码路径:rk3588/kernel-6.1/fs/ext4/balloc.c。文档路径:linuxDoc/fs/ext4/

RK3588 kernel-6.1 fs/ext4/bitmap.c 位图统计与校验和分析

RK3588 kernel-6.1 fs/ext4/bitmap.c 位图统计与校验和分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/bitmap.c(约 99 行)
  • 调用方:balloc.c(块位图 CRC 校验)、ialloc.c(inode 位图 CRC 校验/设置、统计空闲 inode)、mballoc.c(块位图写后更新校验和)、resize.c(扩容后写回 GDT 中校验和)、super.c(调试/统计路径)等

2. 代码作用(一句话)

bitmap.c 提供两样东西:① 对内存中一段 位图缓冲区 统计 置 0 位数(即可分配槽位数);② 在启用 元数据校验和(metadata_csum) 时,对 块位图、inode 位图 计算/校验 CRC32,并与 块组描述符 ext4_group_desc 中存储的 低 16 / 扩展高 16 位比对。


3. 各函数说明

3.1 ext4_count_free

1
2
3
4
unsigned int ext4_count_free(char *bitmap, unsigned int numchars)
{
return numchars * BITS_PER_BYTE - memweight(bitmap, numchars);
}
  • memweight:统计 bitmapnumchars 字节里为 1 的位数(内核通用位操作辅助)。
  • 总 bit 数 − 已用 bit 数(1 的个数)= 空闲 bit 数(0 的个数)

用于在已知位图长度时 快速得到空闲 inode/cluster 数量(例如 ialloc.cext4_count_free_inodesballoc.c 调试路径与 super.c 部分统计)。

3.2 inode 位图校验和

  • ext4_inode_bitmap_csum_verify:若未启用 ext4_has_metadata_csum,直接返回 1(视为通过)。否则用 ext4_chksom(sbi, s_csum_seed, bh->b_data, sz) 计算 CRC,与 gdp->bg_inode_bitmap_csum_lo(及大描述符时的 bg_inode_bitmap_csum_hi 拼成 32 位)比较;小描述符只比较低 16 位(calculated &= 0xFFFF)。
  • ext4_inode_bitmap_csum_set:写位图后把 CRC 写回 GDT 相应字段。

参数 sz 由调用方传入(inode 位图有效字节数随每组合法 inode 数变化),保证 只对有效位图区域 做校验和。

3.3 块位图校验和

  • ext4_block_bitmap_csum_verify / ext4_block_bitmap_csum_set:逻辑与 inode 版相同,区别是 校验长度固定为
    EXT4_CLUSTERS_PER_GROUP(sb) / 8(每组 数 / 8 = 块位图有效字节数,与 bigalloc 一致:位图按 为单位)。
  • 存储字段:bg_block_bitmap_csum_lo / bg_block_bitmap_csum_hi(同样受 s_desc_sizeEXT4_BG_*_CSUM_HI_END 宏约束)。

balloc.cext4_validate_block_bitmap 中调用 ext4_block_bitmap_csum_verify,失败则标记组内块位图损坏。


4. 解决了什么问题

问题 做法
如何 O(字节) 统计位图中 0 bit 数量 ext4_count_freememweight 一次扫完,避免按位循环
静默位图损坏(I/O、存储误码)导致 错误分配/释放 metadata_csum:读位图后先验 CRC,与 GDT 不一致则拒绝信任该位图
写位图后 磁盘元数据自洽 事务提交前 _csum_set 更新 GDT,下次挂载或可校验
兼容 旧/小 组描述符 仅 16 位 CSUM 时只比较低 16;64 字节描述符 可存 完整 32 位 CRC

整体上,本文件把 「位图内容的数学性质」(数 0)和 「位图完整性」(CRC)从 balloc/ialloc/mballoc 大块逻辑里 剥离成独立小模块,便于维护与复用。


5. 与其它文件的关系

1
2
3
4
5
6
7
8
9
10
11
读块位图(balloc.c)
|
v
ext4_block_bitmap_csum_verify(bitmap.c)
|
+--> 失败 → ext4_mark_group_bitmap_corrupted

分配/释放后改位图(mballoc.c / ialloc.c)
|
v
ext4_*_bitmap_csum_set(bitmap.c)→ 写日记/GDT

6. RK3588 / 运维提示

  • 使用 metadata_csumtune2fs/mkfs 特性)时,位图 CRC 是 fsck 与内核在线 发现损坏 的重要防线;损坏常伴随 bad block bitmap checksum 类 dmesg。
  • 未启用 metadata_csum 时,本文件校验函数 恒为「通过」,仅 ext4_count_free 有意义。

7. 小结

维度 结论
作用 位图空闲位统计;块/inode 位图的 metadata CRC 计算与校验
解决问题 高性能计数;启用校验和时 检测位图与组描述符不一致,降低错误分配风险

源码:rk3588/kernel-6.1/fs/ext4/bitmap.c。文档:linuxDoc/fs/ext4/

RK3588 kernel-6.1 fs/ext4/block_validity.c 元数据块保护区分析

RK3588 kernel-6.1 fs/ext4/block_validity.c 元数据块保护区分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/block_validity.c
  • ext4.hstruct ext4_system_blockss_system_blks
  • 挂载选项:block_validity / noblock_validitysuper.c
  • 调用方:inode.c__check_block_validity,以及 ext4_check_blockref

2. 代码作用(一句话)

block_validity.c 在挂载时建立一棵 红黑树,记录全文件系统中所有 仅允许作元数据使用 的物理块区间(超级块备份、GDT、位图、inode 表、以及日记 inode 所映射的数据块等);之后当 inode 的块映射被解释时,若某物理块落在这些区间内且不属于「被允许的 inode」(如 journal inode),则判定为 无效引用,报 损坏 并阻止继续使用错误映射。

文件头注释:

1
2
* Track which blocks in the filesystem are metadata blocks that
* should never be used as data blocks by files or directories.

3. 解决了什么问题

3.1 元数据被误当「文件数据块」

磁盘或软件缺陷可能导致:

  • extent / 间接块中写入的 物理块号 落在 块组位图、inode 表、超级块备份 等区域;
  • 若不做检查,缓冲写或 DIO 可能 覆写元数据,造成 整卷损坏,且比单文件损坏更难恢复。

block validityext4_map_blocks 等路径更新映射前(见 inode.c),对即将采用的 m_pblk 区间 调用检查,重叠系统区则 拒绝-EFSCORRUPTED / ext4_error_inode),把问题 限制在发现点

3.2 日记 inode 的合法占用

Journal 以普通 inode 形式存放,其 文件块 在物理上仍是数据区,但 从「普通文件」视角 这些块应视为 由该保留 inode 专用。因此树节点带 ino

1
2
3
4
5
6
else {
ret = 0;
if (inode)
ret = (entry->ino == inode->i_ino);
break;
}
  • 重叠系统区且 entry->ino == 0:一律 无效(普通文件不得占用)。
  • 重叠且 entry->ino == inode->i_ino允许(例如正在访问的是 journal inode 自身映射)。

ext4_protect_reserved_inodejournal inodeext4_map_blocks 结果逐段 add_system_zone(..., ino),与固定元数据区一起进入同一棵树。

3.3 重叠检测与布局自洽

add_system_zone 在插入 RB 树时若发现新区间与已有区间 几何重叠(非简单相邻合并),返回 -EFSCORRUPTED。这能发现 系统区描述自相矛盾错误重复登记 等异常(例如 ext4_protect_reserved_inode 中 journal 块与已有区重叠会打错误日志)。


4. 核心数据结构

1
2
3
4
5
6
struct ext4_system_zone {
struct rb_node node;
ext4_fsblk_t start_blk;
unsigned int count;
u32 ino;
};
  • start_blk + count:半开或闭区间意义上的 连续物理块(实现里判断用 start_blk + count)。
  • ino == 0通用元数据(位图、inode 表等),任何文件 inode 不可占用
  • ino != 0该 inode 专属 的保留块(当前实际用于 journal inode)。

struct ext4_system_blocks(在 ext4.h):根为 rb_root,挂到 sbi->s_system_blksRCU 指针),读后路径 只读树 + rcu_read_lock


5. 主要流程

5.1 ext4_setup_system_zone(挂载时)

  1. 分配新的 ext4_system_blocks,对每个块组:
    • ext4_num_base_meta_blocks:超级块备份 + GDT 等 基座元数据 连续块;
    • 块位图、inode 位图 各 1 块;
    • inode 表 s_itb_per_group 块。
  2. 若有 journal inode 号ext4_protect_reserved_inode —— igetext4_map_blocks 扫出所有物理块,add_system_zone(..., journal_ino)
  3. 整棵树建完后 rcu_assign_pointer(sbi->s_system_blks, system_blks),避免与 ext4_inode_block_valid 并发读旧指针。

注释强调:sb->s_umount 保护下换人指;读者侧仅用 RCU。

5.2 ext4_release_system_zone(卸载或 noblock_validity

rcu_assign_pointer(..., NULL)call_rcu 延迟释放整棵树(ext4_destroy_system_zonerelease_system_zone),保证 RCU 读者退出后再 kfree

5.3 ext4_sb_block_valid / ext4_inode_block_valid

  • 先做 边界:相对 s_first_data_blockext4_blocks_count无溢出
  • s_system_blks == NULL(未启用 validity),直接视为合法(返回 1)。
  • 否则在 RB 树中查找 [start_blk, start_blk+count) 是否与某系统区 相交
    • 不相交 → 合法
    • 相交 → 默认 不合法ret = 0),除非 inode 非空且 entry->ino == inode->i_ino

5.4 ext4_check_blockref

遍历 __le32 * 块指针数组(最多 max 个),对每个非零块号调用 ext4_inode_block_valid
特例:若当前 inode 即 journal inode整段跳过(日记内部结构自行维护块引用)。

用于 indirect 块、extent 索引块 等结构中 批量校验子块号

5.5 模块 init/exit

ext4_init_system_zone / ext4_exit_system_zone:为 ext4_system_zone 分配 kmem_cachesuper.cext4_init_fs 路径注册)。


6. 挂载选项与默认行为

  • block_validity:默认 开启super.c 注释:默认启用,可用 noblock_validity 关闭)。
  • 关闭后 s_system_blks 为空ext4_sb_block_valid 恒通过略少 CPU / 锁开销,但 失去这类交叉引用检查

7. 与其它模块的关系

1
2
3
4
5
6
7
8
9
10
11
12
挂载(super.c)
|
v
ext4_setup_system_zone(block_validity.c)
|
v
s_system_blks RCU 指针

ext4_map_blocks / 写路径(inode.c)
|
v
check_block_validity → ext4_inode_block_valid

balloc.cext4_num_base_meta_blocks 等用于 计算每组基座元数据块数,与 本文件登记的区间 一致,形成「布局算出来」与「运行期校验」的衔接。


8. RK3588 / 工程注意点

  • 嵌入式上 一般保持默认 block_validity,用较小代价换 更早发现元数据交叉引用
  • 若 profiling 证明热点在极密集小块 IO,可评估 noblock_validity(需接受 失去该层防护)。
  • 出现 invalid block / overlap system zoneext4_error_inode 时,多属 磁盘损坏、旧 bug、或非法离线改动,应 完整备份 + fsck,而非仅忽略。

9. 小结

维度 结论
作用 维护 系统块区 RB 树,在映射块号时校验 不得将元数据区当作普通文件块(journal inode 除外)
解决问题 防止损坏或错误的 inode 指向元数据物理块 导致 元数据被数据写覆盖;尽早报 EFSCORRUPTED
并发 RCU 切换 s_system_blks;释放延迟到 grace period

源码:rk3588/kernel-6.1/fs/ext4/block_validity.c。文档:linuxDoc/fs/ext4/

RK3588 kernel-6.1 fs/ext4/dir.c 目录与遍历机制分析

RK3588 kernel-6.1 fs/ext4/dir.c 目录与遍历机制分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/dir.c
  • namei.clookup/create/rename 等)、hash.cext4_htree_fill_tree 等 htree 填充)、inline.cext4_read_inline_dir)、inode.cext4_map_blocks)的协作

2. 代码作用(一句话)

dir.c 实现 ext4 目录的文件层行为readdir/getdents 遍历(含 htree 哈希目录 与非索引线性目录)、目录项格式合法性校验目录块校验和加密目录名解密后上报inline 目录 回退,以及 ext4_dir_operations(含 llseek/release/ioctl/fsync);大目录通过 ext4_dx_readdir哈希序 缓存到红黑树再 dir_emit,避免纯字节线性扫全块。


3. 解决了什么问题

问题 dir.c 中的处理方式
大目录 readdir 过慢 启用 htreeEXT4_INODE_INDEX 等)时走 ext4_dx_readdir:按 major/minor hash 批量装入 fname 红黑树,顺序 dir_emit;与 hash.c 协作
目录项损坏导致越界/错 inode __ext4_check_dir_entry:检查 rec_len、对齐、与 name_len 关系、不越出 buffer、inode 不超过 s_inodes_count;失败 ext4_error_*
telldir/seekdir 在哈希目录下的语义 ext4_dir_llseekhtreef_pos 表示哈希空间hash2pos/pos2maj_hash/pos2min_hash),兼容 32/64 位 APIFMODE_32BITHASH/FMODE_64BITHASH(如 NFS)
目录块静默损坏 读块后 ext4_dirblock_csum_verify,失败则报错并跳块
加密目录展示明文名 fscrypt_fname_disk_to_usr + dir_emitcasefold 时用条目内 hash/minor_hash
小目录 inline 数据 ext4_read_inline_dir,无则回退映射块
htree 元数据异常 ext4_dx_readdir 若返回 ERR_BAD_DX_DIR:无 metadata_csum 时可 ext4_clear_inode_flag(INDEX) 回退线性读(有风险,仅旧场景)
并发改目录导致 f_pos 悬垂 inode_eq_iversion/inode_query_iversion:目录改变时从块内重扫对齐 offset
fsck/一致性扫描 ext4_check_all_de:对整块顺序校验所有 ext4_dir_entry_2

4. 核心组件

4.1 is_dx_dir

ext4_has_feature_dir_index 前提下,满足 其一 则视为可走目录索引路径:

  • inode 置 EXT4_INODE_INDEX;或
  • 逻辑上仅 1 个目录块i_size == 1 block);或
  • inline data 目录(可能参与转换路径)。

具体 htree 读由 hash.c 等与 ext4_dx_readdir 衔接。

4.2 __ext4_check_dir_entry / is_fake_dir_entry

  • 假项. / ..(名长 1–2 且为点);或 EXT4_FT_DIR_CSUM 校验占位项 —— 不参与最小 ext4_dir_rec_len(1, dir) 的严格名规则(用 fake 分支)。
  • 正常项则按 dircasefold 下的最小记录长度等检查。

返回 0 表示合法1 表示有问题(与历史上 ext2 返回值语义相反,注释已说明)。

4.3 ext4_readdir(非 dx 或 dx 失败回退后的线性路径)

主要步骤:

  1. fscrypt_prepare_readdir(加密目录)。
  2. is_dx_dir:先试 ext4_dx_readdir;仅当 ERR_BAD_DX_DIR 时再尝试清 INDEX 标志并走线性。
  3. ext4_read_inline_dir 处理 inline。
  4. 加密时准备 fscrypt_fname 缓冲。
  5. ctx->pos ext4_map_blocks 定位目录数据块,readaheadext4_bread 读块。
  6. 目录块校验和set_buffer_verified
  7. iversion 不一致时从块头重扫 rec_len 对齐 offset
  8. 循环 ext4_check_dir_entry + dir_emit;洞(err==0)则推进 ctx->posdir_relax_shared 用于并发下让出。

4.4 htree:fname 树与 ext4_dx_readdir

  • struct fname:存 hash、minor_hash、inode、file_type、name(可变长),挂在 rb_hash 上;哈希碰撞 时用链表 next 串在同一 RB 节点逻辑下(少见)。
  • ext4_htree_store_dirent(可被 hash.c 调用):插入/合并碰撞链;加密目录 树内存放 解密后名ent_name)。
  • ext4_dx_readdir:首次分配 dir_private_info;若 f_pos 被用户改写则 清空树 并重解析 curr_hash;优先吐 extra_fname(上次 dir_emit 缓冲区满 未发完的碰撞链);需要时调 ext4_htree_fill_treehash.c)继续装树。
  • ext4_release_dirext4_htree_free_dir_info 释放整棵树。

4.5 hash2pos / ext4_get_htree_eof

  • f_pos 在 dx 目录中编码 (major_hash, minor_hash),不是单纯字节偏移。
  • EOFEXT4_HTREE_EOF_32BITEXT4_HTREE_EOF_64BIT,取决于 f_mode是否 compat 32 位 syscall

4.6 ext4_check_all_de

工具函数:从 buffer 头到尾按 rec_len 跳转,逐项 ext4_check_dir_entry;指针越过 top-EFSCORRUPTED。供 namei/校验路径 快速扫整块目录项。

4.7 ext4_dir_operations

1
2
3
4
5
6
7
8
const struct file_operations ext4_dir_operations = {
.llseek = ext4_dir_llseek,
.read = generic_read_dir,
.iterate_shared = ext4_readdir,
.unlocked_ioctl = ext4_ioctl,
.fsync = ext4_sync_file,
.release = ext4_release_dir,
};

目录 openfile->f_op 指向此处;iterate_shared 为现代 readdir 接口(与 sharedlock 语义配合)。


5. 数据流示意

1
2
3
4
5
6
7
8
9
10
11
12
13
getdents / readdir
|
v
ext4_readdir
|
+--> is_dx_dir && ext4_dx_readdir(hash 序 + RB 树)
| |
| +--> ext4_htree_fill_tree(hash.c)
|
+--> inline:ext4_read_inline_dir(inline.c)
|
+--> 线性:ext4_map_blocks → ext4_bread
→ dirblock csum → check_dir_entry → dir_emit

6. 与 namei.c / hash.c 的分工

文件 侧重
dir.c 只读列举、目录 file_operations、项格式校验辅助
namei.c lookup / create / unlink / rename、路径解析与目录改写的 inode 语义
hash.c dx 索引块 解析、ext4_htree_fill_tree、改名/插入时 htree 维护

7. RK3588 / 工程注意点

  • 大目录(如海量小文件缓存目录)强烈依赖 htree + dir_index;若磁盘上 htree 损坏且 无 metadata_csum,内核可能 降级清 INDEX(注释称不标脏),应以备份+fsck 为主,勿依赖长期使用降级态。
  • 加密 (fscrypt) 与 casefold 目录会走 dir.c 中额外分支,调试 readdir 异常时核对 密钥与编码
  • ext4_check_dir_entry** 告警** 多指向 磁盘目录块损坏不兼容工具写盘,需 dmesg + fsck

8. 小结

维度 结论
作用 目录 readdir/哈希目录遍历目录项与目录块校验f_pos/llseek 语义ext4_dir_operations
解决问题 大目录性能(htree)、损坏检测、crypt/casefold、inline、与 VFS dir_context 对接

源码:rk3588/kernel-6.1/fs/ext4/dir.c。文档:linuxDoc/fs/ext4/

RK3588 kernel-6.1 fs/ext4/ext4_jbd2.c 与 JBD2 对接层分析

RK3588 kernel-6.1 fs/ext4/ext4_jbd2.c 与 JBD2 对接层分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/ext4_jbd2.cext4 ↔ jbd2 的运行时胶水层,约 392 行)
  • 配套声明与宏、事务额度、回调、ext4_handle_valid 等:**ext4_jbd2.h**(本文件大量逻辑依赖其中的宏与 inline)

2. 代码作用(一句话)

ext4_jbd2.c 把 ext4 的元数据修改统一接到 JBD2:开启/结束事务、在「无日志 / fast commit 回放」下用伪 handle** 走同一套 API;封装 get_write_access / get_create_access / dirty_metadata / forget,并在启用 metadata_csum 时为缓冲块挂 jbd2 triggers;同时处理日志已中止整盘只读块设备异步写失败 等边界,保证崩溃一致性与错误可恢复路径一致。**


3. 解决了什么问题

问题 ext4_jbd2.c 中的处理方式
ext4 与 jbd2 API 分散、错误路径不统一 集中实现 __ext4_journal_* / __ext4_forget / __ext4_handle_dirty_metadata,上层通过 ext4_jbd2.h 宏传入 __FILE__/__LINE__ 便于定位
无外部 journal 或 fast commit 回放仍需同一控制流 EXT4_FC_REPLAY 或无 s_journalext4_get_nojournal():用 current->journal_info引用计数(伪装 handle_t *),ext4_handle_valid 据此区分真/假 handle
日志已在后台 abort(如 commit 线程 I/O 错) ext4_journal_check_start:若 is_journal_abortedext4_abort 把整个 FS 只读拉齐,返回 -EROFS,避免在已坏日志上继续提交脏元数据
强制关机 / 只读挂载 ext4_forced_shutdown → -EIOsb_rdonly → -EROFS;并对 SB_FREEZE_COMPLETEWARN_ON
释放块后日志里仍可能重放旧内容(重放死元数据) __ext4_forget:在 ordered/writeback + 元数据或应对 data 记日志 时走 jbd2_journal_revokefull data journal 或非记日志数据块用 jbd2_journal_forget;无日志则 bforget
元数据校验和在事务提交时需重算 __ext4_journal_get_write_access / get_create_accessext4_has_metadata_csumtrigger_type != EXT4_JTR_NONEjbd2_journal_set_triggers,与 s_journal_triggers[] 协作
块设备曾对元数据 buffer 异步写失败,再读旧页写回导致磁盘不一致 ext4_check_bdev_write_error:比较 mapping->wb_errsbi->s_bdev_wb_err,发现新进错误则 ext4_error_err
事务额度不足 __ext4_journal_ensure_credits:无效 handle 直接 0;否则与 ext4_journal_extend(见头文件)配合补足 buffer / revoke 额度
数据日志模式与 delayed allocation、加密的组合 ext4_inode_journal_mode:无 journal 为 writeback;delalloc 开启时 不对普通文件强行 data journal(避免不支持的组合);加密普通文件 固定 ordered,避免 data journal 语义问题
无 journal 路径上 inode 需同步落盘 __ext4_handle_dirty_metadata:无效 handle 时 mark_buffer_dirty,若 inode_needs_syncsync_dirty_buffer 并检查 EIO

4. 核心函数说明

4.1 ext4_inode_journal_mode

根据 s_journal、挂载 DATA_FLAGS、inode EXT4_INODE_JOURNAL_DATA / EA_INODEDELALLOCIS_ENCRYPTED 等,返回三类模式之一:journal dataorderedwriteback
ext4_should_journal_data 等 inline(在 ext4_jbd2.h)判断 __ext4_forget、额度宏等分支。

4.2 ext4_get_nojournal / ext4_put_nojournal

  • current->journal_info 当作 无符号计数(递增/递减),上限 EXT4_NOJOURNAL_MAX_REF_COUNT
  • ext4_handle_valid(计数值小于阈值视为“假 handle”)配对,使 无日志 代码不必遍地 if (journal)

4.3 ext4_journal_check_start

jbd2_journal_start 类操作前统一检查:强制关机只读已冻结日志 abort。失败则不让新事务开始,避免错误扩大。

4.4 __ext4_journal_start_sb / __ext4_journal_stop

  • start:校验通过后,有 journal 且非 FC_REPLAYjbd2__journal_start(带 GFP_NOFS、类型、调用行号等);否则 假 handle
  • stop:假 handle 走 ext4_put_nojournal;真 handle jbd2_journal_stop,合并 handle->h_err 与 stop 返回值,错误走 __ext4_std_error

4.5 __ext4_journal_start_reserved

对已 jbd2 预留额度 的 handle 再 jbd2_journal_start_reserved;若 ext4_journal_check_start 失败则 jbd2_journal_free_reserved 并返回错误指针。

4.6 __ext4_journal_ensure_credits

若当前 buffer credits / revoke credits 不够目标值,计算差额并 ext4_journal_extend(扩展事务占位)。

4.7 ext4_journal_abort_handle

printk 错误上下文后 jbd2_journal_abort_handle,用于 get_*_access / forget / dirty 等失败路径,避免在已失败事务上继续误用。

4.8 ext4_check_bdev_write_error

__ext4_journal_get_write_access 里,若 bh->b_bdev->bd_super 存在,则检查该 bdev 映射上的 写回错误序号,防止脏元数据页与磁盘真相割裂时再次写盘扩大损坏。

4.9 __ext4_journal_get_write_access

  1. 可选 ext4_check_bdev_write_error
  2. 有效 handle:jbd2_journal_get_write_access,失败则 abort handle
  3. 按需 jbd2_journal_set_triggers(metadata checksum)

4.10 __ext4_forget

释元数据或数据块时的日志语义核心

  • 无有效 handlebforget(bh)
  • journal data 模式,或 数据块且不该 journal datajbd2_journal_forget(注释:V1 superblock 不支持 revoke 等历史原因)
  • 否则(元数据,或应对 data journal 的数据):jbd2_journal_revoke(blocknr, bh),失败 ext4_error
  • bh 可为 NULL:仅撤销 journal 中仍存在的块号记录

4.11 __ext4_journal_get_create_access

新分配块的 jbd2_journal_get_create_access + 与写访问相同的 trigger 逻辑。

4.12 __ext4_handle_dirty_metadata

  • 有效 handlejbd2_journal_dirty_metadata,设置 buffer_meta / prio / uptodate;异常时 abort 并 ext4_error_inode(或 pr_err 无 inode)
  • 无效 handlemark_buffer_dirty(_inode);若 inode_needs_sync 则同步写 buffer 并检查 I/O 错误

5. 与 ext4_jbd2.h 的分工(阅读代码时建议一起看)

内容 位置
ext4_journal_start / ext4_journal_stop 等宏 ext4_jbd2.h
ext4_handle_validext4_journal_extendext4_journal_restart 头文件 inline
事务块数估算宏EXT4_SINGLEDATA_TRANS_BLOCKS 等) 头文件
ext4_journal_callback_add 与 commit 后回调 头文件
本文件 上述行为的 .c 实现体(start/stop/forget/dirty/get_access/ensure_credits 与 inode journal modebdev 写错检测

6. 小结

**ext4_jbd2.c** 自身不包含完整日志布局或 recovery 实现(那是 jbd2 与 ext4 super/inode 挂载路径的职责),它负责:在 ext4 元数据路径上正确调用 JBD2(journal_start / get_*_access / dirty_metadata / stop 等)、在有无 journal 下统一抽象、在释放块时选择 revoke/forget、在 metadata checksum 下挂 commit 前触发器、并把 I/O/abort 类失败收敛为 ext4 错误处理。阅读任意 ext4_journal_* / ext4_forget / ext4_handle_dirty_metadata 调用点时,应以本文件与 ext4_jbd2.h 为权威语义说明。

RK3588 kernel-6.1 extents.c 与 extents_status.c 分析

RK3588 kernel-6.1 extents.cextents_status.c 分析

1. 分析范围

文件 行规模(约) 角色
fs/ext4/extents.c 6000+ 盘上 extent B+ 树的查找、插入、分裂/合并、截断拆洞、map_blocks、未写入区段转换、FIEMAP / iomap
fs/ext4/extents_status.c 2300+ 内存中的 extent 状态树(ES tree):延迟块、洞、已写/未写区间的按逻辑块序 RB 树,与 delalloc / bigalloc / fiemap / SEEK_DATA|HOLE 协作;shrinker 回收;pending reservation 子树

配套头文件与概念:ext4_extents.hext4_extentext4_extent_idxext4_ext_path 等)、ext4.hstruct extent_status / ext4_es_tree


2. 两者关系(一句话)

extents.c 维护 inode 内持久化的 extent 索引树(决定块在磁盘上的归属与未写标记);extents_status.c 维护同一 inode 上不写入磁盘的 逻辑区间状态缓存,使延迟分配、大簇、映射查询和空洞语义不必反复扫 page cache 或全盘 extent 树。**


3. extents_status.c:作用与解决的问题

3.1 代码作用(一句话)

为每个 extent 类 inode 建一棵按逻辑起始块 es_lblk 排序的红黑树,节点表示一段连续逻辑块及其 状态(delayed / written / unwritten / hole 等)与可选物理映射;提供 插入、删除、查找、区间扫描、与预留簇计数,并在内存压力下通过 shrinker 丢弃除 delayed 外可丢弃节点。

3.2 解决的问题

问题 做法
FIEMAP、SEEK_DATA/SEEK_HOLE 靠翻 page cache 识别延迟区 在 ES 树中可直接判断某逻辑范围是否为 delayed 或洞
bigalloc 下是否要对簇做 quota 预留 ext4_es_scan_clu / ext4_clu_mapped 等可查簇是否已有 delayed 或已映射,避免重复预留
写回路径为判断 delayed 扫整棵 page cache 查 ES 树 O(log n) + cache_es 热路径近似 O(1)
延迟分配与真实分配、截断、状态不同步 ext4_es_insert_extent / ext4_es_remove_extent / ext4_es_insert_delayed_blockext4_map_blocksinode.c 等成对维护
碎片 extent 导致 ES 树过大 ext4_es_register_shrinker:在压力下回收 written/unwritten/hole 类节点,不回收 delayed(注释:fiemap、bigalloc、seek 仍需要)
bigalloc + delalloc 的「待定簇预留」 另一棵 ext4_pending_tree(pending reservation),ext4_is_pending__insert_pending / __revise_pendingext4_es_insert_delayed_block(..., allocated) 协作,避免簇级预留重复或遗漏
从盘上读出的区间写入 ES(可选缓存) ext4_es_cache_extent:若该范围尚无记录则插入,避免覆盖更新的内存状态

3.3 核心 API(概念)

  • ext4_es_init_tree / ext4_clear_inode_es:挂载/释放 inode 时树生命周期。
  • ext4_es_insert_extent覆盖式更新(先 __es_remove_extent__es_insert_extent),可合并相邻同类 extent;FC_REPLAY 时直接返回。
  • ext4_es_lookup_extent:供 ext4_map_blocks / ext4_da_map_blocks 先查内存;维护 命中/未命中 统计。
  • ext4_es_remove_extent:截断、转换、失效时撕掉区间;bigalloc 下配合 init_rsvd / count_rsvd 调整 reserved cluster 计数。
  • ext4_es_insert_delayed_block:单逻辑块标 DELAYED;若 allocated 则挂 pending。
  • ext4_es_delayed_cluext4_ext_map_blocks 等在非 delalloc 分配路径上扣减「仅 delayed」簇的预留。
  • 并发EXT4_I(inode)->i_es_lock(读写锁)。

3.4 设计注释中的「Step1/Step2」

当前内核已完成「全状态可进树 + shrinker」方向;原文仍保留历史名称 delay extent tree 与向 extent 级加锁 等未来工作的叙述,阅读时以现实现为准。


4. extents.c:作用与解决的问题

4.1 代码作用(一句话)

实现 ext4 的 extent 特性下的盘上多级索引 B+ 树(根可在 inode 内或外部块):查找逻辑块对应的叶 ext4_extent插入/分裂/提升树高合并相邻 extent删除与截断ext4_ext_remove_space)、未写入 extent 与初始化 extent 的分裂与转换,并向 ext4_map_blocks 提供 ext4_ext_map_blocks;同时负责 extent 块校验和损坏校验ext4_ext_check_*)及 FIEMAP / collapse_range / insert_range 等高层次接口。

4.2 解决的问题

问题 做法
大文件用直接/间接块表浪费与低效 用 extent 描述 (逻辑起始, 物理起始, 长度),树高随范围数对数增长
写时分配、块边界与邻居 extent 对齐 ext4_ext_find_goalext4_ext_search_left/rightext4_mb_new_blocks 传入簇对齐后的 goal/logical
bigalloc 下逻辑块与簇 get_implied_cluster_allocEXT4_LBLK_COFF/CMASK 等,在 ext4_ext_map_blocks 中复用已隐含簇
未写入(unwritten)extent 与落盘 查找命中 unwritten 时 ext4_ext_handle_unwritten_extents / ext4_ext_convert_to_initialized 等分裂、转换;direct I/O 结束 ext4_convert_unwritten_io_end_vec
truncate / punch hole / collapse / insert range ext4_ext_remove_space 从叶向上删块、收缩索引;ext4_ext_truncate 更新 i_disksize 并与 remove 路径配合;文件操作层 collapse/insert
事务额度与 i_data_sem 死锁 ext4_datasem_ensure_credits + ext4_ext_trunc_restart_fn:必要时放下 i_data_sem、丢弃预分配、重启事务
元数据块损坏 ext4_extent_block_csum_*ext4_valid_extent*ext4_ext_check_inode
fast commit 重放等特殊路径 ext4_ext_replay_update_exext4_ext_clear_bb 等(与日志重放一致)

4.3 典型调用链(映射)

  1. ext4_ext_map_blocksext4_find_extent 定位路径 → 若在叶 ext4_extent 内则返回 mapped 或走 unwritten 分支 → 若需创建且带 EXT4_GET_BLOCKS_CREATEext4_mb_new_blocksext4_ext_insert_extent
  2. 插入失败时 释放刚分配的簇ext4_discard_preallocations
  3. delalloc 成功路径可 ext4_da_update_reserve_spaceext4_es_delayed_clu 与 ES 树侧预留对齐。

4.4 与 extents_status.c 的衔接(代码层面)

  • ext4_map_blocks(在 inode.c)通常会先 ext4_es_lookup_extent;盘上树路径在 ext4_ext_map_blocks 内仍通过 ext4_find_extent 访问。
  • 分配/转换/截断成功后,extent 代码路径会调用 ext4_es_insert_extentext4_es_remove_extent(具体分散在 extents.cinode.cfast_commit.c 等),保持 ES ↔ 盘树 一致。

4.5 extents.c 内部重要符号(导航)

类别 代表符号
路径与查找 ext4_find_extent(本文件前部)、ext4_ext_search_left/right
修改树形 ext4_ext_insert_extentext4_ext_splitext4_ext_grow_indepthext4_ext_try_to_merge_*
删除 ext4_ext_remove_spaceext4_remove_blocksext4_ext_rm_leaf
映射入口 ext4_ext_map_blocks
用户可见 ext4_fiemapext4_get_es_cache
校验 ext4_ext_check_inodeext4_valid_extent

5. 小结表

维度 extents_status.c extents.c
数据落盘 否(纯内存) 是(inode 或 extent 块)
主要结构 struct extent_status RB 树 + pending 树 ext4_extent_header + index + leaf
核心价值 延迟/洞/映射的快速查询与 bigalloc 预留逻辑 持久块映射与树形维护
典型读者 delalloc、fiemap、seek、shrink 分配器、截断、未写转换、fsck 相关检查

两篇合起来构成 ext4 “内存 extent 视图 + 盘上 extent 索引” 的完整拼图;分析 ext4_map_blocksext4_da_map_blocks 时应同时打开 inode.c 与上述两文件。

RK3588 kernel-6.1 fs/ext4/fast_commit.c 快速提交(Fast Commit)分析

RK3588 kernel-6.1 fs/ext4/fast_commit.c 快速提交(Fast Commit)分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/fast_commit.c(Ext4 细粒度日志 与 JBD2 fast commit 对接:跟踪、落盘、恢复)
  • 盘上 TLV 布局与 tag 定义:fs/ext4/fast_commit.h(注释要求与 e2fsprogs 同源头文件 字节一致
  • JBD2 侧:jbd2_fc_begin_commit / jbd2_fc_end_commit / fallback、replay 回调注册等(本文件只实现 ext4 策略与 j_fc_replay_callback

2. 代码作用(一句话)

在挂载选项允许时,把一次事务里对元数据的改动记成 TLV(tag-length-value)增量日志 追加进 journal 的 fast commit 区;fsync/事务提交时优先只刷这些增量与用户数据,再写 TAIL(CRC + TID) 保证原子可见性;恢复阶段按 TLV 重放目录项、inode 体、extent 增删等 终态,无法覆盖的操作则 ext4_fc_mark_ineligible 回退 完整 jbd2 commit


3. 解决了什么问题

问题 处理方式
传统 full commit 对小块元数据也要整块/大批 buffer 记账,延迟与 I/O 放大 符合条件 的事务只写 紧凑 TLV 序列 + 相关 inode 数据刷盘,减少相对 full commit 的工作量(具体收益依赖负载)
仍需与现有 jbd2 事务模型兼容 ext4_fc_commitjbd2_fc_begin_commit 栅栏内工作;失败走 jbd2_fc_end_commit_fallback → 等价于完成 full transaction
部分 VFS/元数据操作难以表达为安全增量 ext4_fc_mark_ineligible 设置 EXT4_MF_FC_INELIGIBLEs_fc_ineligible_tid,此后直到对应 TID 的提交只做 full commit(见 fast_commit.hEXT4_FC_REASON_*)
崩溃后如何认定一段 fast commit 有效 每条 fast commit 以 EXT4_FC_TAG_TAIL 结束,内含 事务 TID从头到尾 CRCreplay 仅当校验通过;日志里可存在 多个 TAIL(多次 fsync 的示意见文件头注释)
重放须幂等 不记「过程」而记 可重复的终态(如 rename 记成 link/unlink + inode 片段);文件开头用 rm/mv 例子说明
CREATE 需先有 inode 再有目录项 ext4_fc_commit_dentry_updatesCREAText4_fc_write_inode / ext4_fc_write_inode_data 再写 dentry TLV,恢复时可当「无名 inode 再链接」
ADD_RANGE 重放时分配器可能占掉将被指派的块 SCAN 阶段 ext4_fc_record_regions 收集 (ino, lblk, pblk, len)ext4_fc_replay_check_excluded 在回放分配路径中排除这些物理块
与并发更新的竞态 ext4_fc_start_update / ext4_fc_stop_update:某 inode 若在本次 fast commit 列表中,通过 EXT4_STATE_FC_COMMITTING + 等待队列,避免 提交过程中继续改同一 inodeext4_fc_track_templatei_sync_tid 区分同一事务内首次/更新跟踪
日志与数据设备分离 ext4_fc_perform_commit 在写 fast commit 块前对 journal->j_fs_dev blkdev_issue_flush,避免数据未落盘而元数据日志已可见
inode / extent / ES 树在重放时的语义 重放期间 **`s_mount_state

4. TLV 类别(与 fast_commit.h 一致)

Tag 含义(概要)
HEAD feature 掩码 + 本段 fast commit 起始 TID
UNLINK / LINK / CREAT 目录项删除、添加、创建(值体 ext4_fc_dentry_info:父 ino、子 ino、名字)
ADD_RANGE 某 inode 新增 extent(盘上 12 字节 ext4_extent 嵌入)
DEL_RANGE 逻辑块区间删除
INODE 需回放的 raw inodeiblocks 等由重算而非直接信任)
PAD 对齐填充
TAIL CRC + TID,标识本段 fast commit 完整结束

5. 提交路径概要(ext4_fc_perform_commit

文件头注释给出的顺序与实现对应关系:

  1. ext4_fc_submit_inode_data_all / ext4_fc_wait_inode_data_all:参与本次提交的 inode 用户数据先下发并等待(ordered 语义相关)。
  2. 外挂 journal 时 flush 文件系统设备
  3. 若本 TID 下首次 fast commit,写 HEAD TLV。
  4. ext4_fc_commit_dentry_updates:目录项队列刷成 TLV(CREAT 先 inode 再 dentry)。
  5. 遍历 s_fc_q[FC_Q_MAIN]FC_COMMITTING 的 inode:数据inode TLV。
  6. ext4_fc_write_tail:写 TAIL,固化 CRC。

入口 ext4_fc_commitjbd2_fc_begin_commit → 检查 ineligibleext4_fc_perform_commitjbd2_fc_wait_bufsjbd2_fc_end_commit;任一步失败则 jbd2_fc_end_commit_fallback 并更新统计为 FAILED/INELIGIBLE

队列 staging: 若在 full / fast commit 正在进行JBD2_FULL_COMMIT_ONGOING / JBD2_FAST_COMMIT_ONGOING),新跟踪的 inode 与 dentry 进入 FC_Q_STAGING,在 ext4_fc_cleanupspliceMAIN


6. 跟踪入口(与 namei/写路径的关系)

  • ext4_fc_track_link / track_unlink / track_create:目录操作时入 s_fc_dentry_q;加密目录名导致 -EOPNOTSUPPEXT4_FC_REASON_ENCRYPTED_FILENAME
  • ext4_fc_track_inode:inode 元数据变化。
  • ext4_fc_track_range:extent 层逻辑块区间增删。
  • 以上经 ext4_fc_track_template 可将 inode 挂入 s_fc_q,并与 当前 handle 的 TID 绑定。

7. 恢复路径(ext4_fc_replay

  1. PASS_SCANext4_fc_replay_scan 顺序解析 TLV,维护 滚动 CRC,遇到合法 TAILTID/CRC 匹配 则确定 fc_replay_num_tagsADD_RANGE 同时 ext4_fc_record_regions。异常短包/未知 tag 等返回 STOP 或错误码,由 JBD2 决定是否中止本段。
  2. 重放 pass:逐 tag 调用 ext4_fc_replay_*unlink / link / create / add_range / del_range / inode)。计数耗尽后 ext4_fc_set_bitmaps_and_counters 等收尾。
  3. ext4_fc_init:即使 未启用 fast commit 挂载选项,仍注册 j_fc_replay_callback,以便挂载 旧日志 时仍能重放曾写入的 fast commit 块。

清理与 EXT4_FC_REPLAY 复位见 ext4_fc_replay_cleanup(在文件后部,与卸载/重放结束配合)。


8. 文件内记载的 TODO / 限制(阅读源码时值得注意)

  • Replay 路径若全面用 journal handle 做原子化、以及在重放前持久化 FC_REPLAY 超块状态,可进一步加固 重放中途崩溃 的场景(注释 TODO 0)。
  • Fast commit 提交路径曾注释 全文件系统级锁 的代价;后续可更多把 ext4_fc_start/stop_updatejournal_start/stop 对齐以缩小锁粒度(TODO 1)。
  • 更多 ineligible 场景 待覆盖(TODO 2)。

9. 小结

fast_commit.c 不是替代 ext4 的 extent/目录 实现,而是在 JBD2 之上增加一条 「记录终态增量」 的快速提交通道:目录与 inode 变更 + 块区间增删 可在一次 fsync 相关提交中以较少日志体积落盘;通过 TAIL CRC幂等 TLV 设计回放前 SCAN块排除列表 控制正确性;不适用的操作统一 ext4_fc_mark_ineligible 回退 full journal commit。分析某条元数据路径是否走 fast commit,应在业务代码中搜索 ext4_fc_track_*ext4_fc_mark_ineligible,并与本文件的 commit/replay 流程对照。