RK3588 kernel-6.1 page-io.c 与 readpage.c 分析

RK3588 kernel-6.1 page-io.creadpage.c 分析

1. 分析范围

文件 职责
page-io.c 写路径:把 buffer_head 的页 打成 写 BIO 提交;fscrypt 写时加密 bouncebio 完成 时清 buffer_async_writeend_page_writebackext4_io_end/ext4_io_end_vecunwritten extent 写完后 转换元数据(可经 workqueue 延迟
readpage.c 读路径ext4_mpage_readpages 多页 readahead/readpage,尽量 连续块合并为一个读 BIO;读完成后 可选 decrypt → verity 校验mempool + 工作队列);复杂页回退 block_read_full_folio/ext4_get_block

协作:ext4.hext4_map_blocks)、extents.cext4_convert_unwritten_io_end_vec)、fscryptfsverityinode.c/super.crsv_conversion_wq)。


2. page-io.c:作用与解决的问题

2.1 一句话

writeback / 显式回写 场景下,ext4_bio_write_page** 对 已映射、已 dirty 的普通块buffer_async_write,按 设备上连续块fscrypt 可合并 规则 组装 ext4_io_submit 中的写 BIO;完成回调 ext4_end_bio/ext4_finish_bio 处理 错误位、bounce 页释放、写回结束;若写的是 unwritten 预分配区,用 ext4_io_end + io_end_vec 记录区间,在 I/O 全部完成开 journal handle“unwritten → written”(同步 ext4_put_io_end 或异步 ext4_put_io_end_deferi_rsv_conversion_work)。**

2.2 问题对照表

问题 处理方式
页内多块不能先 submit 再标 async 遍历整页 标记 buffer_async_write / 清 dirty,再第二遍 塞进 BIO,避免 先完成的 bh 触发 end_page_writeback 而后面还在组装
i_size 跨页空洞 len < PAGE_SIZEzero_user_segment(注释引用 mmap 语义
洞、delay、unmapped、unwritten 暂不写 跳过 submit;若仍 dirty 则 redirty_page_for_writepage(如 事务提交写回
加密文件落盘必须是密文 fscrypt_encrypt_pagecache_blocksbounce pageBIO 指向 bounce;完成时 fscrypt_free_bounce_page;mempool/死锁用 GFP_NOWAIT先 submit 再重试 策略
多块连续写合并 io_submit_add_bh:块号不连续或 fscrypt 不可合并ext4_io_submit
sync 写 WB_SYNC_ALL 时 BIO 加 REQ_SYNC
bio 完成与 buffer 竞态 b_uptodate_lock 下扫描 是否仍有 buffer_async_write,再决定是否 end_page_writeback
unwritten 转换需 journal 且不能与 truncate 竞态 注释:truncate 等 DIO/PageWriteback;转换在 ext4_free_ioend 路径;失败时 ext4_msg(..., KERN_EMERG, ...) 提示潜在丢数据
大量并发 bio 完成争 io_end EXT4_IO_END_UNWRITTENbio->bi_private 链到 io_end->biorefcount 最后一棒 ext4_put_io_end_defer
journal 存在时避免在 softirq 里长事务 ext4_add_complete_ioi_rsv_conversion_listqueue_work(rsv_conversion_wq)

2.3 主要 API

  • ext4_init_io_end / ext4_get_io_end / ext4_put_io_end(_defer)io_end 生命周期
  • ext4_alloc_io_end_vec / ext4_last_io_end_vec:描述 待转换的逻辑块范围
  • ext4_io_submit_init / ext4_io_submit:写 BIO 批次边界
  • ext4_bio_write_pagewritepages 路径写单页(与 mpage.c/ext4 包装 协作)

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

3.1 一句话

ext4_mpage_readpagespage_size == block_size 的常见形态 下,用 ext4_map_blocks(带 上一段 map 缓存)把一页(或多页 readahead)整理成 连续物理块列表;能合并则 单个大读 BIO;读完成走 mpage_end_io,若需 解密/verity 则分配 bio_post_read_ctx,按序 fscrypt_enqueue_decrypt_workfsverity_enqueue_verify_work,最后 SetPageUptodate/unlock_page;若页 已有 buffer先洞后数据块不连续 等,则 放弃 mpageblock_read_full_folio(..., ext4_get_block)。**

3.2 问题对照表

问题 处理方式
通用 mpage 无法挂 ext4 读完成回调 专用 ext4_mpage_readpages + mpage_end_io
加密文件读回需异步解密 STEP_DECRYPTdecrypt_workfscrypt_decrypt_bio
fs-verity 需在读后验哈希 STEP_VERITYverity_work释放 ctx 防 mempool 递归死锁,再 fsverity_verify_bio
解密与 verity 应用不同 workqueue 注释:verity 可能再读元数据页,又要解密,避免 同一队列递归
POST_READ ctx OOM mempool_create_slab_pool(NUM_PREALLOC_POST_READ_CTXS),分配 不因内存失败
多 bio 完成顺序与一页多 bh 不尝试 复刻 end_buffer_async_read 复杂度;此类页 goto confused
page_size != block_size 易出现 尾洞/非连续回退 block_read_full_folio
verity 下 i_size 与 merkle ext4_readpage_limitverity/in_progress 时用 s_maxbytes,否则 i_size_read
全洞页且 verity fsverity_verify_page 短路;失败用 错误页 路径
fscrypt 读 BIO 元数据 fscrypt_set_bio_crypt_ctx / fscrypt_mergeable_bio 与写路径一致

3.3 初始化

  • ext4_init_post_read_processing / ext4_exit_post_read_processingslab + mempool,由 super.c 模块 __init/__exit 调用。

4. 读与写的分工小结

方向 核心抽象 与 ext4 元数据
合并读 BIO + post-read 管道 ext4_map_blocks 只读映射;不在这里改 extent
合并写 BIO + buffer_head 异步位 普通块 仅刷脏数据;unwrittenext4_convert_unwritten_io_end_vec提交 extent 更新

5. 小结

  • page-io.c 解决 “如何把页缓存安全、可加密、可合并地写到盘,并在 unwritten 分配后把元数据事务化地变成正式写入”
  • readpage.c 解决 “如何在高性能 mpage 读与加密、verity、非平凡页布局之间折衷,必要时回退到 buffer 层单块读”

若需 read_folio/address_space_operationsext4_mpage_readpages 的衔接,可在 file.c/inode.c 中继续追踪。

文章互动

阅读 --

留言

0 条留言

正在加载留言…