如何用bpftrace获取NVMe Trim命令的起始LBA与块数量?
获取NVMe Trim命令的起始LBA与块数量
问题背景
尝试用bpftrace捕获NVMe Trim(数据集管理)命令的起始LBA和块数量,但由于nvme_dsm_range结构存储在PRP/PRP列表中,无法直接解析该结构导致无输出,需要其他可行方法。
可行方案:追踪内核nvme_setup_discard函数
从提供的内核代码可以看到,nvme_setup_discard是内核构建NVMe Trim命令的核心函数,在这个函数里已经直接计算出了每个Trim段的起始LBA(slba)和块数量(nlb),无需去解析PRP中的nvme_dsm_range结构。
核心代码分析
在nvme_setup_discard函数的__rq_for_each_bio循环中,内核会将bio的扇区转换为NVMe的LBA,并计算块数量:
__rq_for_each_bio(bio, req) { u64 slba = nvme_sect_to_lba(ns, bio->bi_iter.bi_sector); u32 nlb = bio->bi_iter.bi_size >> ns->lba_shift; if (n < segments) { range[n].cattr = cpu_to_le32(0); range[n].nlb = cpu_to_le32(nlb); range[n].slba = cpu_to_le64(slba); } n++; }
这里的slba就是Trim的起始逻辑块地址,nlb是对应的块数量,我们可以直接在bpftrace中追踪这些值。
bpftrace脚本示例(直接提取计算值)
下面是直接追踪nvme_setup_discard函数,提取Trim相关数据的脚本:
#!/usr/bin/env bpftrace // 手动定义内核结构(若bpftrace无法自动解析时使用) struct nvme_ns { unsigned int lba_shift; // 其他字段可省略,仅保留需要的部分 }; kprobe:nvme_setup_discard { $ns = (struct nvme_ns *)arg0; $req = (struct request *)arg1; $segments = blk_rq_nr_discard_segments($req); $n = 0; // 遍历每个bio提取数据 $bio = $req->bio; while ($bio != 0) { if ($n < $segments) { $sector = $bio->bi_iter.bi_sector; // 调用内核函数计算LBA,等价于nvme_sect_to_lba $slba = nvme_sect_to_lba($ns, $sector); $size = $bio->bi_iter.bi_size; $nlb = $size >> $ns->lba_shift; printf("Trim 段 %d:\n", $n); printf(" 起始LBA: %llu\n", $slba); printf(" 块数量: %u\n", $nlb); } $bio = $bio->bi_next; $n++; } }
脚本执行效果
执行后会输出每个Trim命令的各段信息,类似:
Trim 段 0: 起始LBA: 1024 块数量: 4 Trim 段 1: 起始LBA: 2048 块数量: 8
备选方案:解析req->special_vec中的nvme_dsm_range
如果想直接解析已经构建好的nvme_dsm_range结构,也可以通过req->special_vec获取其内存地址:
#!/usr/bin/env bpftrace // 手动定义nvme_dsm_range结构 struct nvme_dsm_range { uint64_t slba; uint32_t nlb; uint32_t cattr; }; kprobe:nvme_setup_discard { $req = (struct request *)arg1; $segments = blk_rq_nr_discard_segments($req); // 获取nvme_dsm_range的虚拟地址 $page = $req->special_vec.bv_page; $offset = $req->special_vec.bv_offset; $range = (struct nvme_dsm_range *)(page_address($page) + $offset); for ($i = 0; $i < $segments; $i++) { printf("Trim 段 %d:\n", $i); printf(" 起始LBA: %llu\n", le64_to_cpu($range[$i].slba)); printf(" 块数量: %u\n", le32_to_cpu($range[$i].nlb)); } }
内容的提问来源于stack exchange,提问作者boycechen
相关产品推荐
相关产品推荐

