You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 19:55:35