显式易失性回写缓存控制¶
介绍¶
许多存储设备,尤其是在消费市场中,都带有易失性回写缓存。这意味着设备在数据实际到达非易失性存储之前,就向操作系统发出 I/O 完成信号。 这种行为显然加快了各种工作负载,但这意味着当操作系统执行数据完整性操作(如 fsync、sync 或卸载)时,需要强制将数据刷新到非易失性存储。
Linux 块层提供了两种简单的机制,允许文件系统控制存储设备的缓存行为。 这些机制是强制缓存刷新和请求的强制单元访问 (FUA) 标志。
显式缓存刷新¶
可以将 REQ_PREFLUSH 标志 OR 到从文件系统提交的 bio 的 r/w 标志中,这将确保在实际 I/O 操作开始之前,存储设备的易失性缓存已被刷新。 这明确保证了先前完成的写入请求在标记的 bio 启动之前已存在于非易失性存储上。 此外,可以在其他方面为空的 bio 结构上设置 REQ_PREFLUSH 标志,这只会导致显式缓存刷新,而没有任何依赖的 I/O。 建议使用 blkdev_issue_flush()
辅助函数来进行纯缓存刷新。
强制单元访问¶
可以将 REQ_FUA 标志 OR 到从文件系统提交的 bio 的 r/w 标志中,这将确保仅在数据已提交到非易失性存储后才发出此请求的 I/O 完成信号。
文件系统的实现细节¶
文件系统可以简单地设置 REQ_PREFLUSH 和 REQ_FUA 位,而不必担心底层设备是否需要任何显式缓存刷新以及强制单元访问的实现方式。 REQ_PREFLUSH 和 REQ_FUA 标志都可以设置在单个 bio 上。
块驱动程序的特性设置¶
对于不支持易失性写缓存的设备,不需要驱动程序支持,块层在进入驱动程序之前完成空的 REQ_PREFLUSH 请求,并从具有有效负载的请求中删除 REQ_PREFLUSH 和 REQ_FUA 位。
对于具有易失性写缓存的设备,驱动程序需要通过设置以下内容来告诉块层它支持刷新缓存
BLK_FEAT_WRITE_CACHE
queue_limits 特性字段中的标志。 对于也支持 FUA 位的设备,还需要告诉块层传递 REQ_FUA 位,方法是在 queue_limits 结构的特性字段中也设置
BLK_FEAT_FUA
标志。
基于 bio 的块驱动程序的实现细节¶
对于基于 bio 的驱动程序,如果驱动程序设置了 BLK_FEAT_WRITE_CACHE 标志,则 REQ_PREFLUSH 和 REQ_FUA 位将简单地传递给驱动程序,并且驱动程序需要处理它们。
注意:当 _未_ 设置 BLK_FEAT_FUA 标志时,也会传递 REQ_FUA 位。 任何设置 BLK_FEAT_WRITE_CACHE 的基于 bio 的驱动程序也需要处理 REQ_FUA。
对于重新映射驱动程序,REQ_FUA 位需要传播到底层设备,并且需要为设置了 REQ_PREFLUSH 位的 bio 实现全局刷新。
blk-mq 驱动程序的实现细节¶
当设置 BLK_FEAT_WRITE_CACHE 标志时,带有有效负载的 REQ_OP_WRITE | REQ_PREFLUSH 请求会自动转换为 REQ_OP_FLUSH 请求序列,然后是块层的实际写入。
当设置 BLK_FEAT_FUA 标志时,REQ_FUA 位会简单地传递给 REQ_OP_WRITE 请求;否则,在完成写入请求后,块层会为设置了 REQ_FUA 位的 bio 提交发送 REQ_OP_FLUSH 请求。