概述
amdgpu kfd TTM create GTT
- vm_alloc_memory_of_gpu
- bo_do_create()
- ttm_bo_placement_from_domain()
- ttm_bo_init_reserved()
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信息
- ttm_mem_global_alloc // 根据之前计算好的tbo想彻底填充好页面信息所需要的cpu内存的数量, 申请内存, 这里我们给tbo的zone是kernel的, 所以我们再这个函数内部用kernel_zone申请, 看他的zone现在够不够用,不够的话, 需要swapout原来的(ttm_shrink), 这里, 如果刚才的type是ttm_bo_kernel. 那在lru的后端, 最后实在没办法才会swap?(priority = 1)
- 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
- drm_vma_offset_add // 如果cpu可见内存, 添加到vma, 保证在mmap时,可以到ttm_vm.c正常map给umd
- ttm_bo_validate() tbo区域验证和更新, 确保当前bo满足之前的期望值
bo// 当前的bo
placement// 期望的位置和属性
ctx// ttm操作的资源锁, 给dma_resv用的- 所以使用ttm_bo_mem_compat时, param1: 目标place, parem2: 当前区域, param3: 比较后返回的flag
- ttm_bo_move_buffer() // bo:tbo, placement:期望的位置和flag , ctx:dma_resv
- ttm_bo_mem_space() //通过placement决定我们的bo要往哪挪, 挪的空间也要在这准备好(get_node) mem_type = TTM_PL_SYSTEM, 此后new_mem就有了 mm_node 了
- ttm_bo_handle_move_mem(); bo:将要移动的bo, mem:将要移动的位置, false:不可evict, ctx: 当前操作的ctx
- 目标区或者原始区是pci, 或者两区域的cachde不匹配,那就得将原始区的映射释放并且将原始区内存加锁
- 目标区不是显存区域(TTM_MEMTYPE_FLAG_FIXED),有可能是GTT, 有可能是SYSTEM, 反正都是在系统内存那边,cpu都能访问
- tbo还没有页面存储结构, 需要创建ttm_tt(ps:也就是去系统内存搞点内存为了存后边系统内存的页面) ttm_tt_create 这个只是创建了存储页面的指针数组, 还没真正上页面, 此时创建好的ttm_tt->state = tt_unpopulated ;; ttm_tt_init_fields()
- 设置ttm_tt里面已经有页面的cached属性 ttm_tt_set_page_caching, 你会疑惑, 2.1 步骤不是刚创建ttm_tt? 但是有可能之前就已经有页面, 那需要把之前的页面设置cached属性, 但是2.1刚创建的, ttm->state是ttm_unpopulated, 还没页面,所以只是记录了页面属性,并没设置
- 新区域在非SYSTEM区(那就是GTT), ttm_tt_bind() 虚拟地址和物理地址的绑定 ,期间会发生populate和bind
- ttm_tt的mem进行地址绑定, 因为此时tbo->ttm处于一种需要移动状态, ttm_tt->state 一共是三种状态, bound. unbound, populated, 所以我们不确定当前ttm_tt是啥时候创建的 , 所以需要走一遍populated的过程才能进行bound.
- ttm_tt_populate() // 页面上新
- amdgpu_ttm_tt_populate()
1. 如果是USERPTR的内存, 需要创建SG后备存储页面的结构
2. 如果已经有页面,并且还是SG共享页面, 需要将页面保存到ttm->sg里面,并且还需要再次地址bind
3. ttm_populate_and_map_pages() // 获取页面, map到设备侧-
ttm_pool_populate() //获取后端页面
-
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. 系统内存页面获取完毕
- 申请完页面需要告诉ttm后端的global记账器, 人家可是需要swapout/in的
-
-
tt->dma_address[i] = dma_map_page// 将页面映射给设备. ttm->dma_address存设备视角的总线地址 , 这里有个快速映射的手段,既可以一页一页映射,又可以一片一片的映射
- ttm_tt_add_mapping() 页面对应到设备所在地址映射空间, 在umd进行mmap时有用
-
- ret = ttm->func->bind(ttm, bo_mem); // 驱动自己的wrapper的 amdgpu_ttm_backend_bind
- if userptr
- 来自于umd的page, 可能是, 需要sg存储这些不连续的page
- if !amdgpu_gtt_mgr_has_gart_addr
- 如果当前tbo还没有在gart虚拟地址范围申请地址, 那抱歉, 标记一个假的地址, 返回
- amdgpu_ttm_tt_pte_flags
- 获取当前bo的页表项的标记
- amdgpu_gart_bind
- 绑定到gart表, 不赘述
- if userptr
- 看原来的区域怎么搞: if (bo->mem->mem_type == SYSTEM) // 如果原来区域在系统内存,那好, 该umap就umap, 该释放就是放, vm_bo_invalidate,kunmap, 把老的挪走, 新的换上, 地址用新的mem刚申请的那个地址, 就move完成
- 上边2, 是new不是显存并且old是系统内存(if (bo->mem->mem_type == SYSTEM)) , 如果new的区域不是fix(VRAM)的但是old的是GTT的, 那就需要通知driver的move_notify, 将old的GTT释放, 并且umap,然后再 ttm_bo_move_ttm
- 这里需要注意的是, 原区域是GTT的话, 需要unbind释放页表和占位资源
- 再次标记新ttm_tt的cached属性
- 新的区域如果是GTT, 需要bind页表
- 然后, 就可以新老替换(结构体拷贝), 标记完成, 并且将刚给new的mm_node释放
- 如果new_mem 和 old_mem 在上边的一堆判断中都没法做, 但是驱动有办法, 那就驱动帮忙做
- 如果驱动也不管, 那只好 memcpy_fromio ttm_bo_move_memcpy()
- if (bo->mem.mm_node) 如果刚才的操作已经实现了gpu视角的空间申请
- 获取tbo基于gpu视角的地址, VRAM是物理地址, GTT是虚拟地址或者假地址
- 如果刚才的新的驱动是VRAM的, 因为ttm_tt本身不是vram的表征, 所以释放掉
最后
以上就是欢喜鸭子为你收集整理的amdgpu kfd TTM create GTT的全部内容,希望文章能够帮你解决amdgpu kfd TTM create GTT所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复