09 - SVM缺页处理机制

难度 : 🔴🔴 高级
预计学习时间 : 2.5-3小时
前置知识: 第6-8章、GPU架构、页面异常处理


📋 概述

缺页处理(Page Fault Handling)是SVM最核心的功能之一。当GPU或CPU访问未映射的页面时,会触发页面异常,驱动需要快速恢复访问。想象一下:

  • 🚨 页面异常: GPU访问地址时发现页表无映射
  • ⏸️ XNACK暂停: GPU暂停执行,等待驱动修复
  • 🔧 驱动修复: 查找范围、迁移数据、建立映射
  • ▶️ 重试成功: GPU恢复执行,访问成功

本章深入页面异常的检测、处理和恢复机制。


9.1 XNACK机制

什么是XNACK?

XNACK(Execute No Acknowledge)是AMD GPU的页面错误重试机制:

复制代码
传统GPU(无XNACK):
    访问未映射地址 → VM Fault → GPU挂起 → 无法恢复

XNACK GPU:
    访问未映射地址 → VM Fault → 暂停执行 → 通知CPU
    ↓
    驱动修复映射
    ↓
    GPU重试访问 → 成功!

XNACK硬件支持

  • GFX9及以上架构(Vega, RDNA, CDNA)
  • 需要在进程创建时启用
  • 检查方式:/sys/class/kfd/kfd/nodes/0/capabilities

XNACK工作流程

复制代码
步骤1: GPU shader执行
┌────────────────────────────┐
│ GPU: load [0x12345000]     │ ← 访问虚拟地址
└────────────────────────────┘
            ↓
步骤2: 页表查询
┌────────────────────────────┐
│ GPU MMU: 查找PTE            │
│ 结果: PTE无效或不存在         │
└────────────────────────────┘
            ↓
步骤3: 触发VM Fault
┌────────────────────────────┐
│ 硬件记录:                   │
│ - 故障地址: 0x12345000      │
│ - PASID: 0x8001            │
│ - VMID: 7                  │
│ - 写故障?: false            │
│ - 时间戳: TS                │
└────────────────────────────┘
            ↓
步骤4: 中断CPU
┌────────────────────────────┐
│ GPU → PCIe → CPU IRQ       │
│ 调用: amdgpu_irq_handler()  │
└────────────────────────────┘
            ↓
步骤5: 查找进程和范围
┌────────────────────────────┐
│ svm_range_restore_pages()  │
│ - 根据PASID找进程            │
│ - 根据地址找svm_range        │
└────────────────────────────┘
            ↓
步骤6: 修复映射
┌────────────────────────────┐
│ - 迁移页面(如需要)          │
│ - 更新GPU页表               │
└────────────────────────────┘
            ↓
步骤7: GPU重试
┌────────────────────────────┐
│ GPU: 重新执行load指令        │
│ 结果: 成功!                 │
└────────────────────────────┘

XNACK启用检查

c 复制代码
// 进程创建时
struct kfd_process *p = kfd_create_process(...);

// 检查GPU是否支持XNACK
if (node->adev->gfx.xnack_enabled) {
    p->xnack_enabled = true;
}

// 后续使用
if (!p->xnack_enabled) {
    pr_debug("XNACK not enabled for pasid 0x%x\n", pasid);
    return -EFAULT;  // 不支持页面错误恢复
}

9.2 GPU页面异常处理

主函数:svm_range_restore_pages

这是GPU页面异常的入口函数:

c 复制代码
int svm_range_restore_pages(struct amdgpu_device *adev,
           unsigned int pasid,    // 进程ID
           uint32_t vmid,         // 虚拟机ID
           uint32_t node_id,      // GPU节点
           uint64_t addr,         // 故障地址(页号)
           uint64_t ts,           // 时间戳
           bool write_fault)      // 写故障?

参数来源

  • 从GPU中断处理程序传递
  • addr: 触发故障的虚拟地址(页号)
  • ts: 硬件时间戳,用于检测过时故障
  • write_fault: 是否因写操作触发

处理流程详解

复制代码
┌─────────────────────────────────────────┐
│ 1. 查找进程和GPU节点                      │
│    kfd_lookup_process_by_pasid()        │
│    kfd_node_by_irq_ids()                │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 2. 检查XNACK是否启用                      │
│    if (!p->xnack_enabled) return        │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 3. 获取进程mm_struct                     │
│    mm = get_task_mm(p->lead_thread)     │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 4. 检查时间戳(draining检查)              │
│    if (ts < checkpoint_ts) → 过时故障    │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 5. 查找或创建svm_range                    │
│    svm_range_from_addr()                │
│    或 svm_range_create_unregistered()   │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 6. 检查故障有效性                         │
│    - 检查VMA是否存在                      │
│    - 检查访问权限                         │
│    - 跳过重复故障                         │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 7. 确定最佳恢复位置                        │
│    svm_range_best_restore_location()    │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 8. 迁移页面(如需要)                      │
│    svm_migrate_to_vram() 或             │
│    svm_migrate_vram_to_ram()            │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 9. 验证并映射                             │
│    svm_range_validate_and_map()         │
└─────────────────────────────────────────┘
                ↓
┌─────────────────────────────────────────┐
│ 10. 更新统计信息                          │
│     pdd->faults++                       │
└─────────────────────────────────────────┘

代码分析

c 复制代码
int svm_range_restore_pages(struct amdgpu_device *adev,
             unsigned int pasid,
             uint32_t vmid, uint32_t node_id,
             uint64_t addr, uint64_t ts, bool write_fault)
{
    struct svm_range_list *svms;
    struct svm_range *prange;
    struct kfd_process *p;
    struct kfd_node *node;
    struct mm_struct *mm;
    int32_t best_loc, gpuid, gpuidx;
    bool write_locked = false;
    int r = 0;
    
    // 1. 设备检查
    if (!KFD_IS_SVM_API_SUPPORTED(adev))
        return -EFAULT;
    
    // 2. 查找进程
    p = kfd_lookup_process_by_pasid(pasid, NULL);

    svms = &p->svms;
    
    // 3. 检查是否正在draining(排空故障)
    if (atomic_read(&svms->drain_pagefaults)) {
        pr_debug("page fault handling disabled\n");
        r = 0;
        goto out;
    }
    
    // 4. 查找GPU节点
    node = kfd_node_by_irq_ids(adev, node_id, vmid);

    
    // 5. 获取GPU ID
    if (kfd_process_gpuid_from_node(p, node, &gpuid, &gpuidx)) {
        r = -EFAULT;
        goto out;
    }
    
    // 6. XNACK检查
    if (!p->xnack_enabled) {
        pr_debug("XNACK not enabled for pasid 0x%x\n", pasid);
        r = -EFAULT;
        goto out;
    }
    
    // 7. 获取mm_struct
    mm = get_task_mm(p->lead_thread);
    
    // 8. 加锁(初始为读锁)
    mmap_read_lock(mm);
    
retry_write_locked:
    mutex_lock(&svms->lock);
    
    // 9. 检查时间戳(排空检查)
    if (svms->checkpoint_ts[gpuidx] != 0) {
        if (amdgpu_ih_ts_after_or_equal(ts, svms->checkpoint_ts[gpuidx])) {
            pr_debug("draining retry fault, drop fault\n");
            r = -EAGAIN;
            goto out_unlock_svms;
        } else {
            // 时间戳已经包装,重置checkpoint
            svms->checkpoint_ts[gpuidx] = 0;
        }
    }
    
    // 10. 查找范围
    prange = svm_range_from_addr(svms, addr, NULL);
    if (!prange) {
        // 范围不存在,需要创建
        if (!write_locked) {
            // 需要写锁来创建范围和MMU notifier
            mutex_unlock(&svms->lock);
            mmap_read_unlock(mm);
            mmap_write_lock(mm);
            write_locked = true;
            goto retry_write_locked;
        }
        
        // 创建未注册的范围
        prange = svm_range_create_unregistered_range(node, p, mm, addr);
        if (!prange) {
            r = -EFAULT;
            goto out_unlock_svms;
        }
    }
    
    // 降级锁(如果之前升级了)
    if (write_locked)
        mmap_write_downgrade(mm);
    
    // 11. 锁定范围
    mutex_lock(&prange->migrate_mutex);
    
    // 12. 检查是否应跳过恢复
    if (svm_range_skip_recover(prange)) {
        // 范围正在被删除或修改
        amdgpu_gmc_filter_faults_remove(node->adev, addr, pasid);
        r = 0;
        goto out_unlock_range;
    }
    
    // 13. 跳过重复故障(相同范围的多次故障)
    ktime_t timestamp = ktime_get_boottime();
    if (ktime_before(timestamp,
                    ktime_add_ns(prange->validate_timestamp,
                                AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING))) {
        pr_debug("already restored\n");
        r = 0;
        goto out_unlock_range;
    }
    
    // 14. 检查VMA是否存在
    struct vm_area_struct *vma = vma_lookup(mm, addr << PAGE_SHIFT);
    if (!vma) {
        // VMA已被删除,这是过时故障
        pr_debug("VMA is removed\n");
        r = 0;
        goto out_unlock_range;
    }
    
    // 15. 检查访问权限
    if (!svm_fault_allowed(vma, write_fault)) {
        pr_debug("fault addr 0x%llx no %s permission\n",
                addr, write_fault ? "write" : "read");
        r = -EPERM;
        goto out_unlock_range;
    }
    
    // 16. 确定最佳恢复位置
    best_loc = svm_range_best_restore_location(prange, node, &gpuidx);
    if (best_loc == -1) {
        // GPU没有访问权限
        pr_debug("failed get best restore loc\n");
        r = -EACCES;
        goto out_unlock_range;
    }
    
    pr_debug("best restore 0x%x, actual loc 0x%x\n",
            best_loc, prange->actual_loc);
    
    // 17. SMI事件:页面故障开始
    kfd_smi_event_page_fault_start(node, p->lead_thread->pid,
                                   addr, write_fault, timestamp);
    
    // 18. 对齐迁移范围
    unsigned long size = 1UL << prange->granularity;
    unsigned long start = max_t(unsigned long,
                                ALIGN_DOWN(addr, size), prange->start);
    unsigned long last = min_t(unsigned long,
                              ALIGN(addr + 1, size) - 1, prange->last);
    
    // 19. 迁移(如需要)
    bool migration = false;
    if (prange->actual_loc != 0 || best_loc != 0) {
        if (best_loc) {
            // 迁移到VRAM
            r = svm_migrate_to_vram(prange, best_loc, start, last, mm,
                                   KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU);
            if (r) {
                pr_debug("migrate to vram failed, fallback to RAM\n");
                // 回退到系统内存
                if (prange->actual_loc && prange->actual_loc != best_loc)
                    r = svm_migrate_vram_to_ram(prange, mm, start, last,
                        KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU, NULL);
                else
                    r = 0;
            }
        } else {
            // 迁移到系统RAM
            r = svm_migrate_vram_to_ram(prange, mm, start, last,
                                       KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU, NULL);
        }
        if (r) {
            pr_debug("failed %d to migrate\n", r);
            goto out_migrate_fail;
        } else {
            migration = true;
        }
    }
    
    // 20. 验证并映射
    r = svm_range_validate_and_map(mm, start, last, prange, gpuidx,
                                  false, false, false);
    if (r)
        pr_debug("failed %d to map to gpus\n", r);
    
out_migrate_fail:
    // SMI事件:页面故障结束
    kfd_smi_event_page_fault_end(node, p->lead_thread->pid,
                                addr, migration);
    
out_unlock_range:
    mutex_unlock(&prange->migrate_mutex);
out_unlock_svms:
    mutex_unlock(&svms->lock);
    mmap_read_unlock(mm);
    
    // 更新统计
    if (r != -EAGAIN)
        svm_range_count_fault(node, p, gpuidx);
    
    mmput(mm);
out:
    kfd_unref_process(p);
    
    // 清理重试故障过滤器
    if (r == -EAGAIN) {
        amdgpu_gmc_filter_faults_remove(node->adev, addr, pasid);
        r = 0;
    }
    
    return r;
}

9.3 自动范围创建

未注册范围的处理

当GPU访问从未注册过的地址时,驱动会自动创建SVM范围:

c 复制代码
static struct svm_range *
svm_range_create_unregistered_range(struct kfd_node *node,
                                   struct kfd_process *p,
                                   struct mm_struct *mm,
                                   int64_t addr)

创建流程

复制代码
1. 查找VMA边界
   svm_range_get_range_boundaries()
   ↓
2. 检查冲突
   - 检查是否与VM BO重叠
   - 检查是否与userptr重叠
   ↓
3. 如果重叠,缩小范围到单页
   start = addr
   last = addr
   ↓
4. 创建范围
   prange = svm_range_new(&p->svms, start, last, true)
   ↓
5. 设置属性
   if (is_heap_stack)
       prange->preferred_loc = SYSMEM
   ↓
6. 添加到SVMS
   svm_range_add_to_svms(prange)
   svm_range_add_notifier_locked(mm, prange)

边界计算

c 复制代码
static int svm_range_get_range_boundaries(struct kfd_process *p,
                                         int64_t addr,
                                         unsigned long *start,
                                         unsigned long *last,
                                         bool *is_heap_stack)
{
    struct vm_area_struct *vma;
    unsigned long start_limit, end_limit;
    
    // 1. 查找VMA
    vma = vma_lookup(p->mm, addr << PAGE_SHIFT);
    if (!vma)
        return -EFAULT;
    
    // 2. 检查是否为堆或栈
    *is_heap_stack = vma_is_initial_heap(vma) || vma_is_initial_stack(vma);
    
    // 3. 按粒度对齐
    start_limit = max(vma->vm_start >> PAGE_SHIFT,
                     ALIGN_DOWN(addr, 1UL << p->svms.default_granularity));
    end_limit = min(vma->vm_end >> PAGE_SHIFT,
                   ALIGN(addr + 1, 1UL << p->svms.default_granularity));
    
    // 4. 查找相邻范围,避免重叠
    // 查找addr之后的第一个范围
    node = interval_tree_iter_first(&p->svms.objects, addr + 1, ULONG_MAX);
    if (node)
        end_limit = min(end_limit, node->start);
    
    // 查找addr之前的最后一个范围
    rb_node = rb_prev(&node->rb);
    if (rb_node) {
        node = container_of(rb_node, struct interval_tree_node, rb);
        start_limit = max(start_limit, node->last + 1);
    }
    
    *start = start_limit;
    *last = end_limit - 1;
    
    return 0;
}

示例

复制代码
VMA: [0x1000-0x5000]
粒度: 2MB (0x200页)
故障地址: 0x1234

计算:
    对齐起始: ALIGN_DOWN(0x1234, 0x200) = 0x1200
    对齐结束: ALIGN(0x1235, 0x200) = 0x1400
    
    检查相邻范围:
        前面有范围 [0x1000-0x11FF]
        后面有范围 [0x1500-0x17FF]
    
    最终范围: [0x1200-0x14FF]

9.4 最佳恢复位置选择

svm_range_best_restore_location

决定将页面恢复到哪里(系统RAM还是GPU VRAM):

c 复制代码
static int32_t svm_range_best_restore_location(struct svm_range *prange,
                                              struct kfd_node *node,
                                              int32_t *gpuidx)

决策流程

复制代码
┌─────────────────────────────────────────┐
│ APU且prefer_gtt?                        │
│   → 返回0 (系统内存)                      │
└─────────────────────────────────────────┘
                ↓ 否
┌─────────────────────────────────────────┐
│ preferred_loc == 故障GPU                 │
│ 或 preferred_loc == SYSMEM?              │
│   → 返回preferred_loc                    │
└─────────────────────────────────────────┘
                ↓ 否
┌─────────────────────────────────────────┐
│ preferred_loc是其他GPU                   │
│ 且与故障GPU在同一XGMI hive?               │
│   → 返回preferred_loc                    │
└─────────────────────────────────────────┘
                ↓ 否
┌─────────────────────────────────────────┐
│ 故障GPU在ACCESS位图中?                    │
│   → 返回故障GPU ID                       │
└─────────────────────────────────────────┘
                ↓ 否
┌─────────────────────────────────────────┐
│ 故障GPU在ACCESS_IN_PLACE位图中?           │
│   → actual_loc==0? 返回0                 │
│   → actual_loc GPU与故障GPU同hive?       │
│       返回actual_loc                    │
│   → 否则返回0                            │
└─────────────────────────────────────────┘
                ↓ 否
┌─────────────────────────────────────────┐
│ 无访问权限                                │
│   → 返回-1                               │
└─────────────────────────────────────────┘

代码实现

c 复制代码
static int32_t svm_range_best_restore_location(struct svm_range *prange,
                                              struct kfd_node *node,
                                              int32_t *gpuidx)
{
    struct kfd_node *bo_node, *preferred_node;
    struct kfd_process *p;
    uint32_t gpuid;
    
    p = container_of(prange->svms, struct kfd_process, svms);
    
    // 获取故障GPU的ID
    r = kfd_process_gpuid_from_node(p, node, &gpuid, gpuidx);

    // 1. APU且prefer_gtt
    if (node->adev->apu_prefer_gtt)
        return 0;
    
    // 2. preferred_loc检查
    if (prange->preferred_loc == gpuid ||
        prange->preferred_loc == KFD_IOCTL_SVM_LOCATION_SYSMEM) {
        return prange->preferred_loc;
    }
    
    // 3. preferred_loc是其他GPU,检查XGMI
    if (prange->preferred_loc != KFD_IOCTL_SVM_LOCATION_UNDEFINED) {
        preferred_node = svm_range_get_node_by_id(prange,
                                                 prange->preferred_loc);
        if (preferred_node && svm_nodes_in_same_hive(node, preferred_node))
            return prange->preferred_loc;
    }
    
    // 4. ACCESS权限
    if (test_bit(*gpuidx, prange->bitmap_access))
        return gpuid;
    
    // 5. ACCESS_IN_PLACE权限
    if (test_bit(*gpuidx, prange->bitmap_aip)) {
        if (!prange->actual_loc)
            return 0;  // 系统内存
        
        bo_node = svm_range_get_node_by_id(prange, prange->actual_loc);
        if (bo_node && svm_nodes_in_same_hive(node, bo_node))
            return prange->actual_loc;
        else
            return 0;
    }
    
    // 6. 无访问权限
    return -1;
}

示例场景

复制代码
场景1: 单GPU,有ACCESS权限
    preferred_loc: UNDEFINED
    bitmap_access: GPU0=1
    故障GPU: GPU0
    → 返回: GPU0 (迁移到GPU0 VRAM)

场景2: 多GPU XGMI,preferred_loc设置
    preferred_loc: GPU1
    actual_loc: GPU1
    故障GPU: GPU0
    GPU0和GPU1在同一hive
    → 返回: GPU1 (无需迁移,直接通过XGMI访问)

场景3: ACCESS_IN_PLACE,数据在系统RAM
    bitmap_aip: GPU0=1
    actual_loc: 0 (系统RAM)
    故障GPU: GPU0
    → 返回: 0 (保持在系统RAM,远程访问)

场景4: 无权限
    bitmap_access: 空
    bitmap_aip: 空
    故障GPU: GPU0
    → 返回: -1 (无访问权限,返回错误)

9.5 故障过滤与优化

重复故障检测

避免对同一范围的多次故障进行重复处理:

c 复制代码
// 检查validate_timestamp
ktime_t timestamp = ktime_get_boottime();
if (ktime_before(timestamp,
                ktime_add_ns(prange->validate_timestamp,
                            AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING))) {
    // 这个范围最近刚恢复过,跳过
    pr_debug("already restored\n");
    return 0;
}

AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING

  • 默认值:2ms (2,000,000 ns)
  • 意义:2ms内的重复故障被视为同一次故障

示例

复制代码
时间轴:
T0: GPU访问prange的页0 → 故障1 → 驱动恢复 → validate_timestamp=T0
T0+1ms: GPU访问prange的页1 → 故障2
        检查: T0+1ms < T0+2ms → 跳过(认为是故障1的延续)
T0+3ms: GPU访问prange的页2 → 故障3
        检查: T0+3ms > T0+2ms → 处理(新的故障)

Draining机制

在某些操作期间禁用故障处理:

c 复制代码
// 设置draining标志
atomic_set(&svms->drain_pagefaults, 1);

// 记录checkpoint时间戳
svms->checkpoint_ts[gpuidx] = amdgpu_ih_get_wptr_ts(adev);

// 等待所有排队的故障处理完成
// ...

// 清除draining标志
atomic_set(&svms->drain_pagefaults, 0);

使用场景

  • 进程退出时
  • 大规模内存操作前
  • 驱动重置时

时间戳检查

c 复制代码
if (svms->checkpoint_ts[gpuidx] != 0) {
    if (amdgpu_ih_ts_after_or_equal(ts, svms->checkpoint_ts[gpuidx])) {
        // 故障是在checkpoint之后发生的,排空中
        return -EAGAIN;
    } else {
        // 故障是在checkpoint之前的,已经wrap around
        svms->checkpoint_ts[gpuidx] = 0;
    }
}

硬件故障过滤

GPU硬件维护一个故障过滤器,避免重复中断:

c 复制代码
// 从过滤器中移除故障
amdgpu_gmc_filter_faults_remove(node->adev, addr, pasid);

工作原理

复制代码
GPU发现故障 → 添加到过滤器
    ↓
同样故障再次发生 → 被过滤器阻止 → 不产生中断
    ↓
驱动恢复完成 → 移除过滤器 → 允许重试

9.6 故障统计

统计信息

c 复制代码
struct kfd_process_device {
    // ...
    atomic64_t faults;  // 总故障次数
    atomic64_t page_in; // 迁移到VRAM的页数
    atomic64_t page_out; // 迁移到RAM的页数
};

统计更新

c 复制代码
static void svm_range_count_fault(struct kfd_node *node,
                                 struct kfd_process *p,
                                 int32_t gpuidx)
{
    struct kfd_process_device *pdd;
    
    pdd = kfd_process_device_from_gpuidx(p, gpuidx);
    if (pdd)
        WRITE_ONCE(pdd->faults, pdd->faults + 1);
}

查看统计

bash 复制代码
# 查看进程的SVM统计
cat /sys/kernel/debug/kfd/proc/<pid>/svm_ranges

# 输出示例:
# GPU 0:
#   Faults: 1234
#   Pages in: 5678
#   Pages out: 234

💡 重点提示

  1. XNACK是必需的:没有XNACK,SVM无法处理GPU页面异常。

  2. 自动范围创建:首次访问时自动创建范围,简化用户使用。

  3. 智能恢复位置:根据preferred_loc和访问模式选择最佳位置。

  4. 重复故障优化:2ms窗口内的重复故障被合并。

  5. Draining保护:关键操作期间禁用故障处理,防止竞争。


⚠️ 常见陷阱

陷阱1:"禁用XNACK运行SVM程序"

  • ✅ 正确:确保GPU支持并启用XNACK。

陷阱2:"大量小范围导致频繁故障"

  • ✅ 正确:使用更大的粒度(granularity)减少故障。

陷阱3:"忽略权限检查"

  • ✅ 正确:确保VMA有正确的读写权限。

陷阱4:"未处理-EACCES错误"

  • ✅ 正确:GPU无访问权限时,应用需要修改访问策略。

📝 实践练习

  1. 追踪页面故障

    bash 复制代码
    # 启用页面故障调试
    echo 'file kfd_svm.c line 2972 +p' > /sys/kernel/debug/dynamic_debug/control
    
    # 运行GPU程序
    ./my_svm_app
    
    # 查看故障日志
    dmesg | grep "svm_range_restore_pages"
  2. 查看故障统计

    bash 复制代码
    # 实时监控故障
    watch -n 1 'cat /sys/kernel/debug/kfd/proc/*/svm_ranges | grep Faults'
  3. 思考题

    • 为什么需要2ms的重复故障窗口?
    • Draining机制如何防止竞争?
    • XGMI如何影响最佳恢复位置?
    • 自动范围创建可能带来什么问题?
  4. 性能分析

    bash 复制代码
    # 使用perf追踪页面故障
    perf record -e amdgpu:amdgpu_vm_fault -a
    perf report

📚 本章小结

  • XNACK机制:GPU页面异常重试的硬件支持
  • restore_pages:故障处理的核心函数
  • 自动创建范围:首次访问时自动注册
  • 最佳位置选择:根据策略选择RAM或VRAM
  • 故障优化:重复检测、Draining、硬件过滤
  • 统计监控:跟踪故障和迁移情况

缺页处理是SVM的核心,理解这一机制对优化性能至关重要。


➡️ 下一步

掌握了缺页处理后,下一章我们将学习MMU Notifier集成------如何同步CPU页表变化。


🔗 导航

相关推荐
DeeplyMind1 天前
07 - SVM内存迁移机制
svm·amdgpu·rocm·kfd·rocr
DeeplyMind2 天前
06 - SVM范围管理
svm·amdgpu·rocm·kfd
啊阿狸不会拉杆2 天前
《机器学习导论》第 13 章-核机器
人工智能·python·算法·机器学习·支持向量机·svm·核机器
DeeplyMind3 天前
05 - 进程与SVM的关系
svm·amdgpu·rocm·kfd
DeeplyMind4 天前
03 - AMDGPU驱动架构概览
svm·amdgpu·rocm·kfd
DeeplyMind6 天前
ROCm rocr-libhsakmt分析系列4: HsaMemFlags分析
rocm·rocr·libhsakmt·hsamemflags
DeeplyMind7 天前
附录A:AMDGPU SVM 属性类型
kfd·amdgpu svm
DeeplyMind7 天前
04 - SVM核心数据结构详解
svm·amdgpu·kfd
DeeplyMind9 天前
02 - SVM相关的Linux内核基础
hmm·rocm·kfd·共享虚拟内存·amdgpu svm