Linux btrfs checksum tree与csum查找校验匹配
btrfs使用独立的checksum tree(csum tree)来存储文件数据块的校验和。csum tree是btrfs中一棵特殊的B-tree,其root存储在fs_info->csum_root中。每个csum tree的key类型为BTRFS_EXTENT_CSUM_KEY,key的objectid为文件extent在逻辑地址空间中的起始偏移量,offset为csum条目的序号。
checksum tree的叶子节点存储struct btrfs_csum_item,每个csum_item对应一个sector(通常为4KB)的校验值:
struct btrfs_csum_item {
u8 csum;
} __attribute__ ((__packed__));
csum字段的长度取决于文件系统创建时选择的校验算法。默认使用CRC32C时csum长度为4字节,使用xxhash时长度为8字节,使用sha256时长度为32字节,使用blake2b时长度为64字节。一个16KB的leaf节点中,CRC32C模式可存放约4096个csum_item,覆盖约16MB的数据范围。
写入数据时,btrfs在btrfs_finish_ordered_io()中计算校验和并插入csum tree:
int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered)
{
struct btrfs_fs_info *fs_info = ordered->fs_info;
struct btrfs_root *csum_root = fs_info->csum_root;
struct btrfs_ordered_sum *sums;
u64 logical;
int ret;
logical = ordered->disk_bytenr;
list_for_each_entry(sums, &ordered->list, list) {
ret = btrfs_csum_one_bio(csum_root, ordered->inode,
sums, sums->bytenr);
if (ret)
return ret;
}
ret = insert_ordered_extent_file_extent(csum_root, ordered);
return ret;
}
btrfs_csum_one_bio()接收ordered_io完成时生成的struct btrfs_ordered_sum,其中包含了bio中每个sector的校验值。该函数将这些校验值批量插入csum tree:
int btrfs_csum_one_bio(struct btrfs_root *csum_root,
struct inode *inode,
struct btrfs_ordered_sum *sums,
u64 disk_bytenr)
{
struct btrfs_csum_item *item;
struct btrfs_path *path;
u64 offset;
u64 csum_offset;
int ret;
path = btrfs_alloc_path();
for (offset = 0; offset < sums->len; offset += fs_info->sectorsize) {
csum_offset = offset >> fs_info->sectorsize_bits;
item = btrfs_lookup_csum(csum_root, path,
disk_bytenr + offset, 1);
if (IS_ERR(item)) {
ret = btrfs_insert_csum(csum_root, path,
disk_bytenr + offset,
sums->sums + csum_offset *
csum_size);
} else {
write_extent_buffer(path->nodes[0],
sums->sums + csum_offset * csum_size,
(unsigned long)item, csum_size);
}
btrfs_release_path(path);
}
btrfs_free_path(path);
return 0;
}
btrfs_lookup_csum()在csum tree中搜索指定逻辑地址对应的csum item。如果找到,原地覆盖写入新校验值;如果未找到,调用btrfs_insert_csum()插入新的csum条目。
读取数据时,校验验证由btrfs_check_read_bio()完成。该函数在bio读取完成后被调用,将读取到的数据重新计算校验值并与csum tree中的记录比较:
blk_status_t btrfs_check_read_bio(struct btrfs_fs_info *fs_info,
struct bio *bio, int mirror_num)
{
struct bvec_iter iter;
struct bio_vec *bv;
u32 sectorsize = fs_info->sectorsize;
u64 logical = bio->bi_iter.bi_sector << SECTOR_SHIFT;
u8 *csum_expected;
u8 *csum_found;
int ret;
csum_expected = kzalloc(fs_info->csum_size, GFP_NOFS);
csum_found = kzalloc(fs_info->csum_size, GFP_NOFS);
bio_for_each_segment(bv, bio, iter) {
u64 offset = logical + iter.bi_bvec_done;
u64 cur_offset;
for (cur_offset = offset;
cur_offset < offset + bv->bv_len;
cur_offset += sectorsize) {
ret = btrfs_lookup_csum(fs_info->csum_root,
NULL, cur_offset, csum_expected);
btrfs_csum_data(fs_info, bv->bv_page,
cur_offset - logical, csum_found,
sectorsize);
if (memcmp(csum_expected, csum_found,
fs_info->csum_size)) {
if (mirror_num > fs_info->mirror_num_max)
return BLK_STS_IOERR;
return btrfs_check_read_bio(fs_info, bio,
mirror_num + 1);
}
}
}
kfree(csum_expected);
kfree(csum_found);
return 0;
}
csum查找失败时触发btrfs的镜像切换(mirror switching)机制。对于RAID1/10/5/6配置,btrfs自动尝试从下一个镜像读取相同数据,如果所有镜像校验均失败则返回IO错误。btrfs的读路径中,校验失败计数记录在设备的BTRFS_DEV_STAT_CSUM_ERRS统计项中。
csum tree的查询性能直接影响读写时延。btrfs在csum tree中采用批量查找优化,通过btrfs_lookup_csums_range()一次性查询一个连续逻辑地址范围内的所有校验值,减少B-tree遍历次数:
int btrfs_lookup_csums_range(struct btrfs_root *csum_root,
u64 start, u64 end,
struct list_head *list, int search_commit)
{
struct btrfs_key key;
struct btrfs_path *path;
struct btrfs_csum_item *item;
struct btrfs_ordered_sum *sums;
unsigned long offset;
int ret;
key.objectid = start;
key.type = BTRFS_EXTENT_CSUM_KEY;
key.offset = 0;
path = btrfs_alloc_path();
ret = btrfs_search_slot(NULL, csum_root, &key, path, 0, 0);
while (1) {
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
if (key.objectid > end)
break;
offset = (unsigned long)btrfs_item_ptr(path->nodes[0],
path->slots[0],
struct btrfs_csum_item);
sums = kzalloc(sizeof(*sums) + fs_info->csum_size *
(key.offset - key.objectid) / sectorsize);
read_extent_buffer(path->nodes[0], sums->sums,
offset, item_size);
list_add_tail(&sums->list, list);
ret = btrfs_next_item(csum_root, path);
if (ret)
break;
}
btrfs_free_path(path);
return 0;
}
csum tree的更新采用copy-on-write机制,与btrfs的其他B-tree一致。每次插入新的csum条目时,csum tree的extent buffer被CoW到新位置,更新后的根节点地址在事务提交时写入superblock。
Linux btrfs checksum tree与csum查找校验匹配
张小明
前端开发工程师
从零开始:用迅为iTOP-3568开发板搞定Android11移植(附避坑指南)
从零开始:用迅为iTOP-3568开发板搞定Android11移植(附避坑指南)在嵌入式开发领域,RK3568开发板凭借其强大的四核Cortex-A55处理器和丰富的多媒体处理能力,正成为越来越多开发者的首选平台。而Android11作为目前广泛使用…
运维必备:5分钟用 OpenSSL 命令行为你的网站生成免费 HTTPS 证书(含 CSR、自签名、续期)
运维实战:5分钟掌握OpenSSL命令行生成HTTPS证书全流程每次部署新服务时,最头疼的就是证书问题。上周五临下班前,业务部门突然要求临时搭建一个测试环境,我用了不到3分钟就生成了自签名证书,顺利赶上了六点的末班车。这…
不止于实验:手把手教你封装一个可配置的Verilog与门IP核(Vivado实战)
从实验到工程:打造可配置Verilog与门IP核的完整实战指南在FPGA开发领域,能够将常用逻辑模块封装成可复用的IP核是工程师必备的核心技能之一。本文将以一个看似简单的"与门"为例,带你深入掌握Vivado中IP核封装的完整流程与高级技巧&…
采购里的“帕累托法则”:20%的关键决策决定80%的实验成败
帕累托法则说,80%的效应来自20%的原因。在实验室采购中,这个法则同样适用——20%的关键采购决策,决定了80%的实验成败。哪些是“20%的关键采购决策”?决策一:关键试剂的供应商选择。抗体、酶、细胞因子、转染试剂、血清…
保姆级图解:混合键合(Hybrid Bonding)和传统打线/倒装芯片封装到底差在哪?
混合键合技术:从微凸点到无凸点的封装革命在半导体封装领域,工程师们一直在追求三个看似矛盾的目标:更高的连接密度、更低的信号延迟和更优的散热性能。传统封装技术如打线键合(Wire Bonding)和倒装芯片(Flip Chip)已经接近物理极限ÿ…
告别裸机调试!基于STM32F407的工控板,如何用CH340和串口助手快速打印日志与烧录程序
STM32F407工控板开发实战:从串口调试到高效程序烧录指南第一次拿到STM32F407工控板时,很多开发者都会面临两个最基础却又最关键的问题:如何把程序烧录进板子?如何查看调试日志?这两个看似简单的环节,往往成…