amdgpu kfd TTM create GTT

vm_alloc_memory_of_gpu

flags = ALLOC_MEM_FLAGS_GTT (0x2)

domain = alloc_domain = AMDGPU_GEM_DOMAIN_GTT;
alloc_flags = 0;
byte_align = 1; 实际上在do_create时是按页面对齐
bo_type = ttm_bo_type_device (can mmap / Maybe at SYSTEM/VRAM)

bo_do_create()

已经有tbo(driver wrapper) , 所有bo位置属性在driver的bo中描述, 在ttm_bo_init_reserved时设置到tbo

//计算记账器中需要在系统内存存储多少空间
acc_size = ttm_bo_dma_acc_size(&adev->mman.bdev, size, sizeof(struct amdgpu_bo));
page_size = ALIGN(byte_align, PAGE_SIZE) >>PAGE_SHIFT; //按照页面对齐的页面数目
bo->vm_bo // 标记当前mapping的vmbase , 在 vm_bo_base_init时被设置
bo->preferred_domains // 当前申请的bo 期望的位置
bo->allowed_domain //当前申请的bo 允许的位置, 申请时和preferred相同
bo->flags // 当前申请时的cached属性标记和bo资源标记(clear等)

ttm_bo_placement_from_domain()

// 根据alloc_memory时的domian, 标记 driver bo在相应domain的位置和cached属性
由于我们是GTT, 所以, 当前的place中flag存在 PL_TT, flags看之前alloc时的请求了, 目前假定用0, 所以是cached. 我们就有一个domain,所以 num是1 . 将创建的属性保存到abo中

ttm_bo_init_reserved()

实例化tbo中的mem信息

  1. ttm_mem_global_alloc // 根据之前计算好的tbo想彻底填充好页面信息所需要的cpu内存的数量, 申请内存, 这里我们给tbo的zone是kernel的, 所以我们再这个函数内部用kernel_zone申请, 看他的zone现在够不够用,不够的话, 需要swapout原来的(ttm_shrink), 这里, 如果刚才的type是ttm_bo_kernel. 那在lru的后端, 最后实在没办法才会swap?(priority = 1)
  2. tbo资源设置
	bo->bdev = bdev; // ttm_bo_device

	bo->type = type;  //当前是ttm_bo_device

	bo->num_pages = num_pages; // 多少个PAGE_SIZE的页面, 不是多少字节

	bo->mem.size = num_pages << PAGE_SHIFT; // 多少字节

	bo->mem.mem_type = TTM_PL_SYSTEM; //初始值,mem在cpu这边

	bo->mem.num_pages = bo->num_pages; //当前mem有多少页

	bo->mem.mm_node = NULL; // 当前mem没有分配gpu视角地址空间

	bo->mem.page_alignment = page_alignment; // 内存的页对齐大小

	bo->mem.bus.io_reserved_vm = false; //dma侧内存?

	bo->mem.bus.io_reserved_count = 0;

	bo->moving = NULL; //默认bo肯定没有移动

	bo->mem.placement = (TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED); // 在cpu这边默认属性是SYSTEM. cached

	bo->acc_size = acc_size; //记账器需要在cpu侧报错的tbo的资源大小
	bo->sg = sg; // 有没有dma_address
  1. drm_vma_offset_add // 如果cpu可见内存, 添加到vma, 保证在mmap时,可以到ttm_vm.c正常map给umd
  2. ttm_bo_validate() tbo区域验证和更新, 确保当前bo满足之前的期望值
    bo// 当前的bo
    placement// 期望的位置和属性
    ctx// ttm操作的资源锁, 给dma_resv用的
    1. 所以使用ttm_bo_mem_compat时, param1: 目标place, parem2: 当前区域, param3: 比较后返回的flag
    2. ttm_bo_move_buffer() // bo:tbo, placement:期望的位置和flag , ctx:dma_resv
      1. ttm_bo_mem_space() //通过placement决定我们的bo要往哪挪, 挪的空间也要在这准备好(get_node) mem_type = TTM_PL_SYSTEM, 此后new_mem就有了 mm_node 了
      2. ttm_bo_handle_move_mem(); bo:将要移动的bo, mem:将要移动的位置, false:不可evict, ctx: 当前操作的ctx
        1. 目标区或者原始区是pci, 或者两区域的cachde不匹配,那就得将原始区的映射释放并且将原始区内存加锁
        2. 目标区不是显存区域(TTM_MEMTYPE_FLAG_FIXED),有可能是GTT, 有可能是SYSTEM, 反正都是在系统内存那边,cpu都能访问
          1. tbo还没有页面存储结构, 需要创建ttm_tt(ps:也就是去系统内存搞点内存为了存后边系统内存的页面) ttm_tt_create 这个只是创建了存储页面的指针数组, 还没真正上页面, 此时创建好的ttm_tt->state = tt_unpopulated ;; ttm_tt_init_fields()
          2. 设置ttm_tt里面已经有页面的cached属性 ttm_tt_set_page_caching, 你会疑惑, 2.1 步骤不是刚创建ttm_tt? 但是有可能之前就已经有页面, 那需要把之前的页面设置cached属性, 但是2.1刚创建的, ttm->state是ttm_unpopulated, 还没页面,所以只是记录了页面属性,并没设置
          3. 新区域在非SYSTEM区(那就是GTT), ttm_tt_bind() 虚拟地址和物理地址的绑定 ,期间会发生populate和bind
            1. ttm_tt的mem进行地址绑定, 因为此时tbo->ttm处于一种需要移动状态, ttm_tt->state 一共是三种状态, bound. unbound, populated, 所以我们不确定当前ttm_tt是啥时候创建的 , 所以需要走一遍populated的过程才能进行bound.
            2. ttm_tt_populate() // 页面上新
            3. amdgpu_ttm_tt_populate()
              1. 如果是USERPTR的内存, 需要创建SG后备存储页面的结构
              2. 如果已经有页面,并且还是SG共享页面, 需要将页面保存到ttm->sg里面,并且还需要再次地址bind
              3. ttm_populate_and_map_pages() // 获取页面, map到设备侧
              1. ttm_pool_populate() //获取后端页面
                
                1. ttm_get_pages 真正获取page的地方了, 
                     1.     根据page_flag和caechflag找打对应的pool
                             1.     对于通用的GTT(非UC.WC) , 是使用的默认的PAGE. gfp是ZERO|MAYFAIL, 然后就是去内核高端区alloc_page了
                             2.     对于UC,WC那种的cached, pool是有的, 需要在指定的pool里面申请资源
                    2.   对一指定pool的情况, 如果有乱序页面, 需要进行页面重排swap, 而且对于dma区的申请时 , 可能申请到32M的连续page
                   3.   系统内存页面获取完毕
                  
                2. 申请完页面需要告诉ttm后端的global记账器, 人家可是需要swapout/in的
              2. tt->dma_address[i] = dma_map_page// 将页面映射给设备. ttm->dma_address存设备视角的总线地址 , 这里有个快速映射的手段,既可以一页一页映射,又可以一片一片的映射
                
              3. ttm_tt_add_mapping() 页面对应到设备所在地址映射空间, 在umd进行mmap时有用
            4. ret = ttm->func->bind(ttm, bo_mem); // 驱动自己的wrapper的 amdgpu_ttm_backend_bind
              1. if userptr
                1. 来自于umd的page, 可能是, 需要sg存储这些不连续的page
              2. if !amdgpu_gtt_mgr_has_gart_addr
                1. 如果当前tbo还没有在gart虚拟地址范围申请地址, 那抱歉, 标记一个假的地址, 返回
              3. amdgpu_ttm_tt_pte_flags
                1. 获取当前bo的页表项的标记
              4. amdgpu_gart_bind
                1. 绑定到gart表, 不赘述
          4. 看原来的区域怎么搞: if (bo->mem->mem_type == SYSTEM) // 如果原来区域在系统内存,那好, 该umap就umap, 该释放就是放, vm_bo_invalidate,kunmap, 把老的挪走, 新的换上, 地址用新的mem刚申请的那个地址, 就move完成
        3. 上边2, 是new不是显存并且old是系统内存(if (bo->mem->mem_type == SYSTEM)) , 如果new的区域不是fix(VRAM)的但是old的是GTT的, 那就需要通知driver的move_notify, 将old的GTT释放, 并且umap,然后再 ttm_bo_move_ttm
          1. 这里需要注意的是, 原区域是GTT的话, 需要unbind释放页表和占位资源
          2. 再次标记新ttm_tt的cached属性
          3. 新的区域如果是GTT, 需要bind页表
          4. 然后, 就可以新老替换(结构体拷贝), 标记完成, 并且将刚给new的mm_node释放
      3. 如果new_mem 和 old_mem 在上边的一堆判断中都没法做, 但是驱动有办法, 那就驱动帮忙做
      4. 如果驱动也不管, 那只好 memcpy_fromio ttm_bo_move_memcpy()
      5. if (bo->mem.mm_node) 如果刚才的操作已经实现了gpu视角的空间申请
        1. 获取tbo基于gpu视角的地址, VRAM是物理地址, GTT是虚拟地址或者假地址
      6. 如果刚才的新的驱动是VRAM的, 因为ttm_tt本身不是vram的表征, 所以释放掉
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值