AMD KFD的BO设计分析系列7-2:GPU GART 实现深度解析--绑定机制与性能优化

目录

  • [1. GART 绑定机制](#1. GART 绑定机制)
  • [2. GART 解绑机制](#2. GART 解绑机制)
  • [3. TLB 管理](#3. TLB 管理)
  • [4. 性能优化](#4. 性能优化)
  • [5. 调试与监控](#5. 调试与监控)

GART的基本原理请查看:AMD KFD的BO设计分析系列7-1:GPU GART 实现深度解析--基础架构与工作原理

1. GART 绑定机制

1.1 绑定流程概览

复制代码
ttm_tt_bind()
    ↓
amdgpu_ttm_backend_bind()
    ↓
amdgpu_gart_bind()              ← 本章重点
    ├─> 安全检查
    ├─> 记录映射(调试)
    ├─> amdgpu_gart_map()       ← 更新页表
    │   └─> amdgpu_gmc_set_pte_pde()  ← 硬件特定格式
    ├─> 内存屏障 mb()
    ├─> 刷新 HDP 缓存
    └─> 刷新所有 VM Hub TLB

1.2 amdgpu_gart_bind() 详解

c 复制代码
int amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset,
                     int pages, struct page **pagelist,
                     dma_addr_t *dma_addr, uint64_t flags)
{
    int r, i;

    // ========== 1. 安全检查 ==========
    if (!adev->gart.ready) {
        WARN(1, "trying to bind memory to uninitialized GART!\n");
        return -EINVAL;
    }

    // ========== 2. 记录映射关系(调试) ==========
#ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
    unsigned t, p;
    t = offset / AMDGPU_GPU_PAGE_SIZE;
    p = t / AMDGPU_GPU_PAGES_IN_CPU_PAGE;
    for (i = 0; i < pages; i++, p++)
        adev->gart.pages[p] = pagelist ? pagelist[i] : NULL;
#endif

    // ========== 3. 页表未映射则跳过 ==========
    if (!adev->gart.ptr)
        return 0;

    // ========== 4. 更新页表 ==========
    r = amdgpu_gart_map(adev, offset, pages, dma_addr, flags,
                        adev->gart.ptr);

    // ========== 5. 内存屏障 ==========
    mb();  // 确保所有 PTE 写入完成

    // ========== 6. 刷新 HDP 缓存 ==========
    amdgpu_asic_flush_hdp(adev, NULL);

    // ========== 7. 刷新所有 VM Hub 的 TLB ==========
    for (i = 0; i < adev->num_vmhubs; i++)
        amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);

    return 0;
}

1.3 参数详解

参数 类型 含义 示例
adev struct amdgpu_device * GPU 设备 -
offset uint64_t GART 起始偏移(字节) 0x80000000
pages int 要绑定的 CPU 页数 256 (1MB)
pagelist struct page ** CPU 页面指针数组 ttm->pages
dma_addr dma_addr_t * DMA 地址数组 ttm_dma->dma_address
flags uint64_t PTE 标志位 READ|WRITE|WC

offset 计算示例:

c 复制代码
// ttm_tt 绑定到 GART
struct amdgpu_ttm_tt *gtt = ...;
uint64_t offset = (u64)bo_mem->start << PAGE_SHIFT;

// bo_mem->start 是页面索引
// 左移 PAGE_SHIFT 得到字节偏移
// 例如: start=2048, PAGE_SHIFT=12 → offset=0x800000 (8MB)

1.4 amdgpu_gart_map() - 页表填充

c 复制代码
int amdgpu_gart_map(struct amdgpu_device *adev, uint64_t offset,
                    int pages, dma_addr_t *dma_addr, uint64_t flags,
                    void *dst)
{
    uint64_t page_base;
    unsigned t, i;

    // 转换为 GPU 页索引
    t = offset / AMDGPU_GPU_PAGE_SIZE;

    // 遍历每个 CPU 页
    for (i = 0; i < pages; i++) {
        page_base = dma_addr[i];  // CPU 页的 DMA 地址

        // 一个 CPU 页可能包含多个 GPU 页
        // 例如:64KB CPU 页 = 16 个 4KB GPU 页
        for (unsigned j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++) {
            // 调用硬件特定函数设置 PTE
            amdgpu_gmc_set_pte_pde(adev, dst, t,
                                   page_base, flags);
            
            page_base += AMDGPU_GPU_PAGE_SIZE;  // 下一个 GPU 页
            t++;  // 下一个 PTE 索引
        }
    }
    return 0;
}

处理 CPU 页 vs GPU 页差异:

复制代码
CPU 页 (64KB):
┌────────────────────────────────────────────────┐
│         DMA Address: 0xF0000000                │
└────────────────────────────────────────────────┘
                    ↓ 拆分为 16 个 GPU 页
GPU 页 (4KB 每个):
┌─────┬─────┬─────┬─────┬───┬─────┐
│ 0K  │ 4K  │ 8K  │ 12K │...│ 60K │
└─────┴─────┴─────┴─────┴───┴─────┘
  ↓     ↓     ↓     ↓         ↓
PTE[t] PTE[t+1] ...        PTE[t+15]

每个 PTE 存储:
PTE[t+0] = 0xF0000000 | flags
PTE[t+1] = 0xF0001000 | flags
PTE[t+2] = 0xF0002000 | flags
...
PTE[t+15] = 0xF000F000 | flags

1.5 amdgpu_gmc_set_pte_pde() - 硬件 PTE 格式

c 复制代码
void amdgpu_gmc_set_pte_pde(struct amdgpu_device *adev,
                            void *cpu_pt_addr,
                            uint32_t gpu_page_idx,
                            uint64_t addr,
                            uint64_t flags)
{
    uint64_t *pte = (uint64_t *)cpu_pt_addr;
    
    // 组合物理地址和标志位
    pte[gpu_page_idx] = (addr & AMDGPU_PTE_ADDR_MASK) | flags;
}

PTE 格式(64位):

复制代码
 63                                                    12 11        0
┌──────────────────────────────────────────────────────┬──────────┐
│         Physical Address (52 bits)                   │  Flags   │
│         页对齐 (低 12 位为 0)                          │ (12 bits)│
└──────────────────────────────────────────────────────┴──────────┘

标志位详解:

位域 名称 含义
[0] VALID PTE 有效 1
[1] READABLE 可读 1
[2] WRITEABLE 可写 1
[3] EXECUTE 可执行(某些 GPU) 0/1
[4:5] CACHE 缓存策略 00=UC, 01=WC, 10=Cached
[6] SNOOPED Cache coherent 0/1
[7] SYSTEM 系统内存 1
[8:11] FRAG Fragment size 0-9 (4KB-2GB)

标志位计算示例:

c 复制代码
uint64_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev,
                                 struct ttm_tt *ttm,
                                 struct ttm_mem_reg *mem)
{
    uint64_t flags = 0;
    
    // 基础权限
    flags |= AMDGPU_PTE_VALID;      // 位 0
    flags |= AMDGPU_PTE_READABLE;   // 位 1
    
    if (ttm->page_flags & TTM_PAGE_FLAG_WRITE)
        flags |= AMDGPU_PTE_WRITEABLE;  // 位 2
    
    // 缓存策略
    switch (ttm->caching_state) {
    case tt_cached:
        flags |= AMDGPU_PTE_SNOOPED;  // Cache coherent
        break;
    case tt_wc:
        flags |= AMDGPU_PTE_WC;       // Write-combine
        break;
    case tt_uncached:
        // 无额外标志,默认 uncached
        break;
    }
    
    // 系统内存标记
    flags |= AMDGPU_PTE_SYSTEM;
    
    return flags;
}

实际 PTE 值示例:

复制代码
物理地址: 0x00000000F0123000
标志位:   VALID | READABLE | WRITEABLE | WC | SYSTEM
         = 0x00000007

PTE = 0x00000000F0123000 | 0x00000007
    = 0x00000000F0123007

GPU 读取此 PTE 后:
- 访问 GPU VA → 转换到物理地址 0xF0123000
- 权限: 可读可写
- 缓存: Write-combine 模式

1.6 内存屏障与缓存刷新

(1) mb() - 内存屏障
c 复制代码
mb();  // Memory Barrier

作用:

  • 确保之前的所有内存写入完成
  • 防止 CPU 乱序执行导致 PTE 未写入就刷新 TLB
  • x86 编译为 mfence 指令

必要性示例:

c 复制代码
// 错误:无内存屏障
pte[0] = addr0 | flags;
pte[1] = addr1 | flags;
// CPU 可能乱序执行,先刷新 TLB
flush_tlb();  // PTE 可能还在 CPU cache

// 正确:有内存屏障
pte[0] = addr0 | flags;
pte[1] = addr1 | flags;
mb();  // 强制完成所有写入
flush_tlb();  // PTE 已写入内存
(2) amdgpu_asic_flush_hdp() - HDP 刷新
c 复制代码
amdgpu_asic_flush_hdp(adev, NULL);

HDP (Host Data Path):

  • CPU 访问 VRAM 的数据路径
  • 有自己的写缓存
  • 需要显式刷新确保 VRAM 可见

工作流程:

复制代码
CPU 写入 PTE
    ↓
CPU Cache
    ↓ (mb() 刷新)
HDP Write Cache  ← 仍在这里
    ↓ (flush_hdp() 刷新)
VRAM (页表)
    ↓
GPU 可见

实现(硬件特定):

c 复制代码
void amdgpu_asic_flush_hdp(struct amdgpu_device *adev,
                           struct amdgpu_ring *ring)
{
    if (adev->asic_funcs->flush_hdp)
        adev->asic_funcs->flush_hdp(adev, ring);
    
    // 典型实现:
    // WREG32(mmHDP_MEM_COHERENCY_FLUSH_CNTL, 1);
    // 等待完成
}

2. GART 解绑机制

2.1 解绑流程

c 复制代码
int amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset, int pages)
{
    unsigned t, p, i;

    // ========== 1. 安全检查 ==========
    if (!adev->gart.ready)
        return -EINVAL;

    // ========== 2. 清除调试数组 ==========
#ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
    t = offset / AMDGPU_GPU_PAGE_SIZE;
    p = t / AMDGPU_GPU_PAGES_IN_CPU_PAGE;
    for (i = 0; i < pages; i++, p++)
        adev->gart.pages[p] = NULL;
#endif

    // ========== 3. 页表未映射则跳过 ==========
    if (!adev->gart.ptr)
        return 0;

    // ========== 4. 用 dummy page 填充 PTE ==========
    t = offset / AMDGPU_GPU_PAGE_SIZE;
    for (i = 0; i < pages; i++) {
        for (unsigned j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++) {
            amdgpu_gmc_set_pte_pde(adev, adev->gart.ptr, t,
                                   adev->dummy_page_addr,
                                   AMDGPU_PTE_VALID | AMDGPU_PTE_READABLE);
            t++;
        }
    }

    // ========== 5. 刷新机制(同绑定) ==========
    mb();
    amdgpu_asic_flush_hdp(adev, NULL);
    for (i = 0; i < adev->num_vmhubs; i++)
        amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);

    return 0;
}

2.2 为什么用 Dummy Page?

对比方案:

方案 PTE 值 GPU 访问行为 优缺点
清零 PTE 0x0 (Invalid) 页面错误/崩溃 GPU hang 风险
Dummy Page dummy_addr | VALID 读到零值 安全降级
保留旧映射 原地址 读到已释放页 内存安全问题

实际场景:

c 复制代码
// BO 解绑过程中 GPU 可能仍在访问
ttm_tt_unbind(ttm);
    ↓
amdgpu_gart_unbind();  // 用 dummy page 填充
    ↓
GPU 仍在执行着色器,访问已解绑地址
    ↓
读到 dummy page (全零) → 安全
    vs.
读到无效地址 → GPU hang/崩溃 ❌

Dummy Page 标志:

c 复制代码
flags = AMDGPU_PTE_VALID | AMDGPU_PTE_READABLE;
// 注意:
// - VALID: GPU 不会产生页面错误
// - READABLE: 允许读取(返回零)
// - 无 WRITEABLE: 写操作被忽略或产生错误(取决于硬件)

3. TLB 管理

3.1 TLB 概述

TLB (Translation Lookaside Buffer):

  • GPU 的页表缓存
  • 存储最近使用的 PTE
  • 避免每次访问都查页表

层级结构:

复制代码
GPU 内存访问
    ↓
L1 TLB (每个 CU/Shader Engine)
    ↓ Miss
L2 TLB (每个 VM Hub)
    ↓ Miss
Page Table Walk (访问 VRAM 中的页表)
    ↓
获取 PTE

3.2 VM Hub 架构

什么是 VM Hub?

  • GPU 有多个独立的虚拟内存管理单元
  • 每个引擎有自己的 Hub 和 TLB

典型配置(RDNA/CDNA):

Hub ID 名称 用途 TLB 大小
0 GFX Hub 图形引擎 256 entries
1 MM Hub 多媒体(视频编解码) 128 entries
2 VC Hub VCN (视频编码) 64 entries

为什么需要多个 Hub?

  • 并行处理:图形和视频可同时运行
  • 隔离性:不同引擎的内存访问互不干扰
  • 性能:每个 Hub 专门优化

3.3 TLB 刷新机制

c 复制代码
void amdgpu_gmc_flush_gpu_tlb(struct amdgpu_device *adev,
                              uint32_t vmid,
                              uint32_t vmhub,
                              uint32_t flush_type)
{
    // 调用硬件特定函数
    if (adev->gmc.gmc_funcs->flush_gpu_tlb)
        adev->gmc.gmc_funcs->flush_gpu_tlb(adev, vmid, vmhub, flush_type);
}

参数说明:

参数 含义 典型值
vmid 虚拟机 ID(上下文 ID) 0-15
vmhub VM Hub 索引 0=GFX, 1=MM
flush_type 刷新类型 0=全部, 1=部分

硬件实现示例(MMIO 寄存器):

c 复制代码
// Vega/RDNA 系列
static void gmc_v9_0_flush_gpu_tlb(struct amdgpu_device *adev,
                                   uint32_t vmid,
                                   uint32_t vmhub,
                                   uint32_t flush_type)
{
    // 1. 准备刷新命令
    uint32_t req = VM_INVALIDATE_REQ;
    
    // 2. 写入刷新请求寄存器
    WREG32_NO_KIQ(hub->vm_inv_eng0_req + eng, req);
    
    // 3. 等待确认
    for (i = 0; i < adev->usec_timeout; i++) {
        ack = RREG32_NO_KIQ(hub->vm_inv_eng0_ack + eng);
        if (ack & (1 << vmid))
            break;
        udelay(1);
    }
    
    if (i >= adev->usec_timeout)
        DRM_ERROR("Timeout waiting for VM flush ACK!\n");
}

3.4 TLB 刷新时机

操作 是否刷新 TLB 原因
绑定 ✅ 是 新 PTE 需要可见
解绑 ✅ 是 旧 PTE 需要失效
修改标志 ✅ 是 PTE 内容改变
只读 PTE ❌ 否 无状态变化
GPU 重置 ✅ 是 清空所有缓存

刷新开销:

复制代码
单次刷新时间:   ~10-50 微秒
影响范围:       停止所有内存访问
批量优化:       合并多次刷新 → 1 次

3.5 批量刷新优化

问题:

c 复制代码
// 绑定 1000 个页面
for (i = 0; i < 1000; i++) {
    amdgpu_gart_bind(adev, offset + i * PAGE_SIZE, 1, ...);
    // 每次都刷新 TLB → 1000 次刷新!
}

优化:

c 复制代码
// 批量绑定
amdgpu_gart_bind(adev, offset, 1000, ...);
// 只刷新一次 TLB → 性能提升 100x

实现机制:

c 复制代码
int amdgpu_gart_bind(...)
{
    // 更新所有 PTE
    for (i = 0; i < pages; i++)
        update_pte(i);
    
    mb();  // 确保所有 PTE 写入完成
    
    // 只刷新一次
    flush_tlb();  // ← 批量操作的关键
}

4. 性能优化

4.1 地址转换延迟

TLB 命中 vs 未命中:

场景 延迟 说明
L1 TLB 命中 1-2 cycles 最快
L2 TLB 命中 10-20 cycles 较快
Page Table Walk 100-200 cycles 访问 VRAM
VRAM 页表在系统内存 1000+ cycles 极慢(不应发生)

影响:

复制代码
高 TLB 命中率 (95%+):
  平均延迟 = 0.95 * 2 + 0.05 * 100 = 6.9 cycles 

低 TLB 命中率 (50%):
  平均延迟 = 0.50 * 2 + 0.50 * 100 = 51 cycles 

4.2 优化策略对比表

优化方法 实现 性能提升 适用场景 难度
大页支持 使用 2MB/1GB 页 10-100x TLB 效率 大型连续 BO
批量刷新 合并 TLB 刷新 5-10x 绑定速度 多页面绑定
预取优化 连续地址访问 2-3x 带宽利用 线性访问模式 硬件自动
TLB 预热 提前绑定热点数据 减少首次访问延迟 已知访问模式
GART 分区 分离不同用途区域 减少冲突 多引擎并行

4.3 大页支持详解

页面大小对比:

页大小 TLB Entry 覆盖 需要的 Entry 数(1GB 数据)
4KB 4KB 262,144
2MB 2MB 512
1GB 1GB 1

PTE Fragment 字段:

c 复制代码
// 在 PTE 标志中指定
flags |= AMDGPU_PTE_FRAG(frag_size);

// frag_size 编码:
// 0 = 4KB (2^12)
// 1 = 8KB (2^13)
// 2 = 16KB (2^14)
// ...
// 9 = 2MB (2^21)

启用大页的条件:

  1. CPU 页大小 >= 目标大页大小
  2. 物理地址对齐到大页边界
  3. 连续的物理内存

实现示例:

c 复制代码
// 检查是否可用 2MB 页
if (num_pages >= 512 &&  // 至少 2MB
    (dma_addr & 0x1FFFFF) == 0 &&  // 2MB 对齐
    is_contiguous(dma_addr, 512)) {  // 连续
    
    flags |= AMDGPU_PTE_FRAG(9);  // 使用 2MB 页
    // 只需 1 个 PTE 覆盖 512 个 4KB 页
}

4.4 GART 地址空间布局优化

典型布局(优化后):

复制代码
GPU 虚拟地址空间:
┌─────────────────────────────┐ 0x0000000000000000
│   保留区域 (1GB)             │
├─────────────────────────────┤ 0x0000000040000000
│   GART - 图形专用 (4GB)      │ ← GFX Hub 主要区域
│   - Staging buffers         │   TLB 命中率高
│   - 纹理溢出                 │
├─────────────────────────────┤ 0x0000000140000000
│   GART - 计算专用 (4GB)      │ ← Compute 隔离区域
│   - Userptr                 │   减少 TLB 冲突
│   - Compute 输入/输出        │
├─────────────────────────────┤ 0x0000000240000000
│   GART - 视频专用 (2GB)      │ ← MM Hub 专用
│   - DMA-BUF                 │
│   - 视频帧缓冲                │
├─────────────────────────────┤ 0x00000002C0000000
│   AGP Aperture (可选)        │
├─────────────────────────────┤ 0x0000000300000000
│   VRAM Aperture             │ ← 显存直接映射
│   (物理 VRAM 大小)           │   无 TLB 转换
└─────────────────────────────┘

分区优势:

  • 不同引擎访问不同区域 → 减少 TLB 冲突
  • 专用区域可预热 TLB
  • 便于监控和调试

4.5 性能监控

关键指标:

指标 正常值 异常值 问题
TLB 命中率 >95% <80% 访问模式分散
Page Walk 次数 <5% 访问 >20% TLB 太小或抖动
GART 利用率 <80% >95% 接近耗尽
绑定延迟 <1ms/1000页 >10ms 可能碎片化

5. 调试与监控

5.1 Debugfs 接口

bash 复制代码
# 查看 GART 信息
cat /sys/kernel/debug/dri/0/amdgpu_gtt_mm

# 输出示例:
# GART: size=16384MB, used=2048MB, free=14336MB
# num_gpu_pages: 4194304
# num_cpu_pages: 4194304
# table_size: 33554432 (32MB)

详细映射信息(CONFIG_DRM_AMDGPU_GART_DEBUGFS):

bash 复制代码
cat /sys/kernel/debug/dri/0/amdgpu_gart_pages | head -20

# 输出:
# [0] 0xF0000000 (CPU page 0x...)
# [1] 0xF0001000 (CPU page 0x...)
# [2] NULL (unmapped)

5.2 Trace Points

bash 复制代码
# 启用 GART 绑定跟踪
echo 1 > /sys/kernel/debug/tracing/events/amdgpu/amdgpu_gart_bind/enable

# 运行应用
./my_gpu_app

# 查看跟踪日志
cat /sys/kernel/debug/tracing/trace

# 示例输出:
# amdgpu_gart_bind: offset=0x80000000 pages=256 flags=0x7
# amdgpu_gart_bind: offset=0x80100000 pages=512 flags=0x7

5.3 常见问题诊断

问题 1: "GART bind failed"

原因:

  • GART 未初始化(ready=false)
  • DMA 地址无效
  • 页表未映射

诊断:

bash 复制代码
dmesg | grep -i gart

# 查找:
# - "GART: num cpu pages ..." (初始化日志)
# - "Failed to allocate GART table" (分配失败)
# - "trying to bind memory to uninitialized GART" (未就绪)
问题 2: GPU Hang (TLB 相关)

症状:

  • GPU 访问违例
  • dmesg 显示 "VM fault"

诊断:

bash 复制代码
# 查看 VM fault 日志
dmesg | grep -i "vm fault"

# 输出示例:
# [drm:amdgpu_job_timedout] *ERROR* VM fault (0x01) at page 0x80001234
# 
# 分析:
# - 地址 0x80001234 在 GART 范围内
# - 检查该地址是否已绑定
# - 可能是 TLB 未刷新或 PTE 错误

验证 TLB 刷新:

c 复制代码
// 添加调试日志
printk("Flushing TLB for vmhub %d\n", i);
amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);
printk("TLB flush completed\n");
问题 3: 性能下降

诊断步骤:

  1. 检查 TLB 命中率(需要性能计数器)

  2. 查看 GART 使用率

    bash 复制代码
    cat /sys/kernel/debug/dri/0/amdgpu_gtt_mm
    # 如果 used > 95%,可能频繁换页
  3. 监控绑定频率

    bash 复制代码
    # 统计 1 秒内的绑定次数
    perf record -e probe:amdgpu_gart_bind -a sleep 1
    perf report

5.4 调试技巧

技巧 1: 验证 PTE 内容

c 复制代码
void dump_pte(struct amdgpu_device *adev, uint64_t offset)
{
    uint64_t *pte = (uint64_t *)adev->gart.ptr;
    unsigned idx = offset / AMDGPU_GPU_PAGE_SIZE;
    
    printk("PTE[%u] = 0x%016llx\n", idx, pte[idx]);
    printk("  Physical: 0x%016llx\n", pte[idx] & ~0xFFF);
    printk("  Valid: %d\n", !!(pte[idx] & AMDGPU_PTE_VALID));
    printk("  Readable: %d\n", !!(pte[idx] & AMDGPU_PTE_READABLE));
    printk("  Writeable: %d\n", !!(pte[idx] & AMDGPU_PTE_WRITEABLE));
}

技巧 2: 强制 TLB 刷新测试

c 复制代码
// 测试 TLB 刷新是否有效
amdgpu_gart_bind(...);
// 故意不刷新 TLB
// 然后访问 → 应该失败(证明 TLB 缓存了旧值)

amdgpu_gart_bind(...);
flush_all_tlb();  // 强制刷新
// 访问 → 应该成功

技巧 3: 监控 HDP 刷新

c 复制代码
// 测试 HDP 缓存影响
write_pte(...);
// 不刷新 HDP
read_from_gpu();  // 可能读到旧 PTE

write_pte(...);
flush_hdp();
read_from_gpu();  // 应该读到新 PTE

小结

关键要点

  1. 绑定机制

    • 三层函数:bind → map → set_pte_pde
    • 必须刷新 mb、HDP、TLB
    • 处理 CPU 页 vs GPU 页差异
  2. TLB 管理

    • 多个 VM Hub,各自独立 TLB
    • 刷新开销大,需要批量优化
    • 命中率直接影响性能
  3. 性能优化

    • 大页减少 TLB 压力 10-100x
    • 批量刷新减少开销 5-10x
    • 地址空间分区减少冲突
  4. 调试方法

    • Debugfs 查看状态
    • Trace points 跟踪操作
    • 性能计数器监控 TLB
相关推荐
Zeku1 天前
20251130 - 详细解析Framebuffer应用编程中涉及到的API函数
linux·驱动开发·嵌入式软件·linux应用开发
LUCIFER1 天前
[驱动之路(七)——Pinctrl子系统]学习总结,万字长篇,一文彻底搞懂Pinctrl子系统(含Pin Controller驱动框架解析)
linux·驱动开发
Zeku2 天前
20251129 - 详细解析Linux的mmap(内存映射)
linux·驱动开发·嵌入式软件·linux应用开发
少年、潜行2 天前
F1C100/200S学习笔记(1)-- 核心板和验证板硬件设计
linux·驱动开发·f1c200s
Molesidy2 天前
【Linux】基于Imx6ull Pro开发板和platform_device+platform_driver框架的LED驱动设计以及上机测试
linux·驱动开发
黑不溜秋的2 天前
驱动开发系列74 - GPU中的I2C
驱动开发
Zeku2 天前
20251127 - 韦东山Linux - 通用Makefile解析
linux·驱动开发·嵌入式软件·linux应用开发
LYFlied2 天前
规范驱动开发(SDD)主流工具与框架深度解析
驱动开发·ai编程·sdd
春日见2 天前
在虚拟机上面无法正启动机械臂的控制launch文件
linux·运维·服务器·人工智能·驱动开发·ubuntu