RK3588 kernel-6.1 ext4 与块设备 I/O 下发路径
1. 结论(先记住)
ext4 不直接调用具体存储驱动(如某 MMC/SD/NVMe 驱动的符号);它通过 struct super_block 关联的 sb->s_bdev(struct block_device),使用内核统一的:
buffer_head+submit_bh(元数据、大量基于块的读写在 ext2/3/4 传统路径中仍很普遍)struct bio+submit_bio(页缓存写回、ext4_mpage_readpages等多页读)
块层再将请求交给挂在该 bdev 上的 队列与驱动。日志文件系统场景下,jbd2 还会对 journal 设备/分区 单独发 I/O。
2. buffer_head 路径(典型:超级块、inode 表块、目录块、xattr 块等)
sb_getblk/sb_getblk_gfp:按 文件系统内逻辑块号 绑定buffer_head,bh->b_bdev即sb->s_bdev。- 读:
ext4_read_bh/ext4_read_bh_nowait(fs/ext4/super.c)内部submit_bh(REQ_OP_READ | flags, bh)。 - 写:缓冲区经
mark_buffer_dirty、jbd2_journal_dirty_metadata等;直接或经 journal 最终submit_bh(REQ_OP_WRITE | …)(例如super.c写 super、fast_commit.c的ext4_fc_submit_bh、mmp.c等)。
ext4_sb_bread:__ext4_sb_bread_gfp → sb_getblk_gfp + ext4_read_bh_lock,读元数据块的标准入口之一。
与 inode 表:inode.c 中 __ext4_get_inode_loc 使用 sb_getblk(sb, block) 拿到 inode 表所在磁盘块,再 ext4_read_bh_nowait 等读入。
3. bio 路径(典型:用户数据 readpage/writepage)
| 场景 | 文件 | 行为 |
|---|---|---|
| 写回 | page-io.c |
ext4_io_submit → submit_bio(bio),可按 WB_SYNC_ALL 加 REQ_SYNC |
| 多页读 / readahead | readpage.c |
ext4_mpage_readpages 组 REQ_OP_READ bio,末尾 submit_bio(bio) |
bio 上设置 bi_bdev、起始扇区、end_io;fscrypt 时另有 bounce 页 与 fscrypt_set_bio_crypt_ctx 等。
4. 设备级原语(仍经 bdev,不进 ext4 私有驱动)
sync_blockdev(sb->s_bdev):super.c卸载等路径,整设备缓存与队列协调刷写。blkdev_issue_flush(sb->s_bdev):fsync.c、fast commit、部分分配路径,向设备发 flush,配合 FUA/排序 语义。__blkdev_issue_discard等:mballoc.c等 discard/TRIM 路径。
5. jbd2 与 ext4 的分工
有 journal 时,大量元数据 先记入 journal,由 jbd2 对 j_fs_dev / 外部 journal bdev 下发 I/O;ext4 业务路径里常见的是 ext4_fsblk_t 映射 + get_write_access/dirty_metadata,而不是每个 buffer 都由 ext4 直接 submit_bh 写最终位置。
6. 调试阅读顺序建议
super.c:ext4_read_bh/submit_bh、ext4_sb_breadinode.c:__ext4_get_inode_loc(块设备上 inode 表块 的定位与读)page-io.c:ext4_io_submit、ext4_end_bioreadpage.c:submit_bio、读完成链fs/block_dev.c、drivers/scsi、drivers/mmc等:块层与 SoC 存储栈(不在fs/ext4内)
7. 小结
ext4 只认 block_device 与块层抽象;RK3588 上无论是 eMMC、UFS 还是 NVMe,只要在挂载时 同一个 bdev 上挂了文件系统,ext4 的 submit_bh/submit_bio 就会由 Linux 块子系统 路由到对应驱动。排查 I/O 问题时,应同时看 ext4 栈 与 blktrace/驱动队列。
正在加载留言…