系列:Xe 驱动中
drm_gpuvm的实现分析深度定位:架构 + 关键路径。本篇聚焦结构内嵌、类型转换与 flags 复用。
1. C 语言里的"继承":内嵌 + container_of
Linux 内核没有 C++ 的继承机制,但通过 "把父类结构作为子类的第一个成员" + container_of 宏,可以实现等价效果。drm_gpuvm 框架就是围绕这一模式设计的------它的注释里明确写着:
Typically, this structure is embedded in bigger driver structures.
Xe 严格遵循这个约定,形成三层平行的"父---子"结构对:
| 核心层(父) | Xe(子) | 语义 |
|---|---|---|
struct drm_gpuvm |
struct xe_vm |
整个 GPU 地址空间 |
struct drm_gpuva |
struct xe_vma |
单个 VA 映射 |
struct drm_gpuva_op |
struct xe_vma_op |
单条 bind 操作 |
#mermaid-svg-YW44h5pzTsFJySa2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YW44h5pzTsFJySa2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YW44h5pzTsFJySa2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YW44h5pzTsFJySa2 .error-icon{fill:#552222;}#mermaid-svg-YW44h5pzTsFJySa2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YW44h5pzTsFJySa2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YW44h5pzTsFJySa2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YW44h5pzTsFJySa2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YW44h5pzTsFJySa2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YW44h5pzTsFJySa2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YW44h5pzTsFJySa2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YW44h5pzTsFJySa2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YW44h5pzTsFJySa2 .marker.cross{stroke:#333333;}#mermaid-svg-YW44h5pzTsFJySa2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YW44h5pzTsFJySa2 p{margin:0;}#mermaid-svg-YW44h5pzTsFJySa2 g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-YW44h5pzTsFJySa2 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-YW44h5pzTsFJySa2 .cluster-label text{fill:#333;}#mermaid-svg-YW44h5pzTsFJySa2 .cluster-label span{color:#333;}#mermaid-svg-YW44h5pzTsFJySa2 .cluster-label span p{background-color:transparent;}#mermaid-svg-YW44h5pzTsFJySa2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YW44h5pzTsFJySa2 .cluster text{fill:#333;}#mermaid-svg-YW44h5pzTsFJySa2 .cluster span{color:#333;}#mermaid-svg-YW44h5pzTsFJySa2 .nodeLabel,#mermaid-svg-YW44h5pzTsFJySa2 .edgeLabel{color:#131300;}#mermaid-svg-YW44h5pzTsFJySa2 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-YW44h5pzTsFJySa2 .label text{fill:#131300;}#mermaid-svg-YW44h5pzTsFJySa2 .labelBkg{background:#ECECFF;}#mermaid-svg-YW44h5pzTsFJySa2 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-YW44h5pzTsFJySa2 .classTitle{font-weight:bolder;}#mermaid-svg-YW44h5pzTsFJySa2 .node rect,#mermaid-svg-YW44h5pzTsFJySa2 .node circle,#mermaid-svg-YW44h5pzTsFJySa2 .node ellipse,#mermaid-svg-YW44h5pzTsFJySa2 .node polygon,#mermaid-svg-YW44h5pzTsFJySa2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YW44h5pzTsFJySa2 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 g.clickable{cursor:pointer;}#mermaid-svg-YW44h5pzTsFJySa2 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-YW44h5pzTsFJySa2 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-YW44h5pzTsFJySa2 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-YW44h5pzTsFJySa2 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-YW44h5pzTsFJySa2 .dashed-line{stroke-dasharray:3;}#mermaid-svg-YW44h5pzTsFJySa2 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-YW44h5pzTsFJySa2 #compositionStart,#mermaid-svg-YW44h5pzTsFJySa2 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #compositionEnd,#mermaid-svg-YW44h5pzTsFJySa2 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #dependencyStart,#mermaid-svg-YW44h5pzTsFJySa2 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #dependencyStart,#mermaid-svg-YW44h5pzTsFJySa2 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #extensionStart,#mermaid-svg-YW44h5pzTsFJySa2 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #extensionEnd,#mermaid-svg-YW44h5pzTsFJySa2 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #aggregationStart,#mermaid-svg-YW44h5pzTsFJySa2 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #aggregationEnd,#mermaid-svg-YW44h5pzTsFJySa2 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #lollipopStart,#mermaid-svg-YW44h5pzTsFJySa2 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 #lollipopEnd,#mermaid-svg-YW44h5pzTsFJySa2 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YW44h5pzTsFJySa2 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-YW44h5pzTsFJySa2 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YW44h5pzTsFJySa2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YW44h5pzTsFJySa2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YW44h5pzTsFJySa2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内嵌为首成员
内嵌为首成员
内嵌为首成员
xe_vm
drm_gpuvm gpuvm
xe_vma
drm_gpuva gpuva
xe_vma_op
drm_gpuva_op base
drm_gpuvm
drm_gpuva
drm_gpuva_op
2. 三对结构的内嵌细节
2.1 xe_vm ⊃ drm_gpuvm
gpuvm 是 xe_vm 的第一个字段 ,见 xe_vm_types.h:
c
struct xe_vm {
/** @gpuvm: base GPUVM used to track VMAs */
struct drm_gpuvm gpuvm;
/** @svm: Shared virtual memory state */
struct { struct drm_gpusvm gpusvm; ... } svm;
...
};
放在首位不是硬性要求(
container_of支持任意偏移),但放首位可让&xe_vm == &xe_vm.gpuvm,转型时偏移为 0,也便于阅读。
2.2 xe_vma ⊃ drm_gpuva
c
struct xe_vma {
/** @gpuva: Base GPUVA object */
struct drm_gpuva gpuva;
...
u8 tile_mask; // 期望在哪些 tile 建立绑定
u8 tile_present; // 哪些 tile 已建立
u8 tile_invalidated; // 哪些 tile 的绑定已失效
struct xe_vma_mem_attr attr; // PAT/atomic/首选内存位置
};
父结构 drm_gpuva 提供了所有与硬件无关 的字段,见 drm_gpuvm.h:
c
struct drm_gpuva {
struct drm_gpuvm *vm; // 所属地址空间
struct drm_gpuvm_bo *vm_bo; // (VM,BO) 连接点(第 5 篇)
enum drm_gpuva_flags flags; // 标志位(含用户位,见 §4)
struct { u64 addr; u64 range; } va; // VA 起址 + 长度
struct { u64 offset; struct drm_gem_object *obj; ... } gem; // 背后 BO
struct { struct rb_node node; ... } rb; // 区间树节点
};
2.3 xe_vma_op ⊃ drm_gpuva_op
c
struct xe_vma_op {
struct drm_gpuva_op base; // ← 父:MAP/REMAP/UNMAP/PREFETCH 通用描述
struct list_head link; // 挂到 xe_vma_ops.list
enum xe_vma_op_flags flags; // COMMITTED / PREV_COMMITTED / NEXT_COMMITTED
u8 tile_mask;
union { // 按 op 类型区分的私有数据
struct xe_vma_op_map map;
struct xe_vma_op_remap remap;
struct xe_vma_op_prefetch prefetch;
struct xe_vma_op_map_range map_range;
...
};
};
base 由核心层的 split/merge 算法填充(第 6 篇),union 里的私有字段由 Xe 在解析阶段填充(第 7 篇)。
3. 转型辅助:一组 container_of 内联函数
Xe 把所有"子←→父"转型封装成内联函数,集中在 xe_vm.h:
c
// gpuvm → xe_vm
static inline struct xe_vm *gpuvm_to_vm(struct drm_gpuvm *gpuvm)
{ return container_of(gpuvm, struct xe_vm, gpuvm); }
// gpuva → xe_vma
static inline struct xe_vma *gpuva_to_vma(struct drm_gpuva *gpuva)
{ return container_of(gpuva, struct xe_vma, gpuva); }
// gpuva → xe_vm(跨两层:先取 gpuva->vm,再转型)
static inline struct xe_vm *gpuva_to_vm(struct drm_gpuva *gpuva)
{ return gpuvm_to_vm(gpuva->vm); }
// drm_gpuva_op → xe_vma_op
static inline struct xe_vma_op *gpuva_op_to_vma_op(struct drm_gpuva_op *op)
{ return container_of(op, struct xe_vma_op, base); }
// xe_vma → xe_vm(经由内嵌 gpuva 的 vm 指针)
static inline struct xe_vm *xe_vma_vm(struct xe_vma *vma)
{ return container_of(vma->gpuva.vm, struct xe_vm, gpuvm); }
使用场景对照:
- 核心层回调把
struct drm_gpuvm *传回给 Xe → 用gpuvm_to_vm()拿回xe_vm; - 遍历区间树得到
struct drm_gpuva *→ 用gpuva_to_vma()拿回xe_vma; - 遍历 op 列表得到
struct drm_gpuva_op *→ 用gpuva_op_to_vma_op()拿回xe_vma_op。
这正是第 1 篇提到的"双向转型":核心层只认父类型,Xe 在边界处转回子类型。
4. 字段访问器:屏蔽父结构布局
Xe 没有让代码到处直接写 vma->gpuva.va.addr,而是包了一层访问器(并在文档注释里说明目的是"便于将来改实现"),见 xe_vm.h:
c
static inline u64 xe_vma_start(struct xe_vma *vma) { return vma->gpuva.va.addr; }
static inline u64 xe_vma_size(struct xe_vma *vma) { return vma->gpuva.va.range; }
static inline u64 xe_vma_end(struct xe_vma *vma) { return xe_vma_start(vma) + xe_vma_size(vma); }
static inline u64 xe_vma_bo_offset(struct xe_vma *vma){ return vma->gpuva.gem.offset; }
static inline struct xe_bo *xe_vma_bo(struct xe_vma *vma)
{
return !vma->gpuva.gem.obj ? NULL :
container_of(vma->gpuva.gem.obj, struct xe_bo, ttm.base); // 又一次继承转型!
}
注意 xe_vma_bo() 里再次出现继承模式:drm_gem_object 内嵌在 ttm_buffer_object 里,ttm_buffer_object 又内嵌在 xe_bo 里,于是 gem.obj → xe_bo 也靠 container_of。
5. flags 复用:把驱动私有状态塞进 DRM_GPUVA_USERBITS
这是 Xe 用 drm_gpuvm 的一处巧思。核心层在 drm_gpuva.flags 里只定义了 3 个位,见 drm_gpuvm.h:
c
enum drm_gpuva_flags {
DRM_GPUVA_INVALIDATED = (1 << 0), // 背后 BO 已失效
DRM_GPUVA_SPARSE = (1 << 1), // 稀疏映射(NULL 映射)
DRM_GPUVA_USERBITS = (1 << 2), // ← 用户可用位起点
};
从 DRM_GPUVA_USERBITS(bit 2)往上,全部留给驱动。Xe 就在这里定义了 11 个私有标志,见 xe_vm_types.h:
c
#define XE_VMA_READ_ONLY DRM_GPUVA_USERBITS // bit 2 只读
#define XE_VMA_DESTROYED (DRM_GPUVA_USERBITS << 1) // bit 3 已销毁
#define XE_VMA_ATOMIC_PTE_BIT (DRM_GPUVA_USERBITS << 2) // bit 4 原子 PTE
#define XE_VMA_PTE_4K (DRM_GPUVA_USERBITS << 3) // bit 5 ┐
#define XE_VMA_PTE_2M (DRM_GPUVA_USERBITS << 4) // bit 6 ├ 当前 PTE 页尺寸
#define XE_VMA_PTE_1G (DRM_GPUVA_USERBITS << 5) // bit 7 │
#define XE_VMA_PTE_64K (DRM_GPUVA_USERBITS << 6) // bit 8 ┘
#define XE_VMA_PTE_COMPACT (DRM_GPUVA_USERBITS << 7) // bit 9 紧凑 PTE
#define XE_VMA_DUMPABLE (DRM_GPUVA_USERBITS << 8) // bit 10 可被 coredump
#define XE_VMA_SYSTEM_ALLOCATOR (DRM_GPUVA_USERBITS << 9) // bit 11 CPU 地址镜像/SVM
#define XE_VMA_MADV_AUTORESET (DRM_GPUVA_USERBITS << 10)// bit 12 madvise 自动复位
好处 :这些状态与每个 VMA 一一对应,直接复用父结构已有的 flags 字段,无需在 xe_vma 里额外加成员,省内存也省一次间接访问。
对应的判定函数直接读 flags,见 xe_vm.h:
c
static inline bool xe_vma_read_only(struct xe_vma *vma)
{ return vma->gpuva.flags & XE_VMA_READ_ONLY; }
static inline bool xe_vma_is_null(struct xe_vma *vma)
{ return vma->gpuva.flags & DRM_GPUVA_SPARSE; } // 直接复用核心层的 SPARSE 位
static inline bool xe_vma_is_cpu_addr_mirror(struct xe_vma *vma)
{ return vma->gpuva.flags & XE_VMA_SYSTEM_ALLOCATOR; }
注意
xe_vma_is_null()复用的是核心层 的DRM_GPUVA_SPARSE,而is_cpu_addr_mirror()用的是驱动私有 的XE_VMA_SYSTEM_ALLOCATOR------一个映射的语义由"核心位 + 用户位"共同决定。
6. VMA 的三种形态与再一层继承:userptr
通过组合上述 flags,Xe 的 xe_vma 有四种互斥形态,判定逻辑见 xe_vm.h:
| 形态 | 判定 | 背后存储 |
|---|---|---|
| BO 映射 | gem.obj != NULL |
一个 xe_bo |
| NULL / sparse | DRM_GPUVA_SPARSE |
无(读 0 写弃) |
| CPU 地址镜像 | XE_VMA_SYSTEM_ALLOCATOR |
SVM 按需缺页 |
| userptr | 无 BO 且非 null 非镜像 | 用户进程内存页 |
c
static inline bool xe_vma_is_userptr(struct xe_vma *vma)
{
return xe_vma_has_no_bo(vma) && !xe_vma_is_null(vma) &&
!xe_vma_is_cpu_addr_mirror(vma);
}
其中 userptr 又引入第二层继承 ------它在 xe_vma 之外再包一层 xe_userptr_vma,见 xe_vm_types.h 和 xe_vm.h:
c
struct xe_userptr_vma {
struct xe_vma vma; // ← 父
struct xe_userptr userptr; // 额外的 userptr 状态(mmu notifier 等)
};
static inline struct xe_userptr_vma *to_userptr_vma(struct xe_vma *vma)
{
xe_assert(xe_vma_vm(vma)->xe, xe_vma_is_userptr(vma));
return container_of(vma, struct xe_userptr_vma, vma); // 又一次 container_of
}
于是形成一条三级继承链:
#mermaid-svg-1Un3U0ZiDa95gD7P{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1Un3U0ZiDa95gD7P .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1Un3U0ZiDa95gD7P .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1Un3U0ZiDa95gD7P .error-icon{fill:#552222;}#mermaid-svg-1Un3U0ZiDa95gD7P .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1Un3U0ZiDa95gD7P .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1Un3U0ZiDa95gD7P .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1Un3U0ZiDa95gD7P .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1Un3U0ZiDa95gD7P .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1Un3U0ZiDa95gD7P .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1Un3U0ZiDa95gD7P .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1Un3U0ZiDa95gD7P .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1Un3U0ZiDa95gD7P .marker.cross{stroke:#333333;}#mermaid-svg-1Un3U0ZiDa95gD7P svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1Un3U0ZiDa95gD7P p{margin:0;}#mermaid-svg-1Un3U0ZiDa95gD7P g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-1Un3U0ZiDa95gD7P g.classGroup text .title{font-weight:bolder;}#mermaid-svg-1Un3U0ZiDa95gD7P .cluster-label text{fill:#333;}#mermaid-svg-1Un3U0ZiDa95gD7P .cluster-label span{color:#333;}#mermaid-svg-1Un3U0ZiDa95gD7P .cluster-label span p{background-color:transparent;}#mermaid-svg-1Un3U0ZiDa95gD7P .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1Un3U0ZiDa95gD7P .cluster text{fill:#333;}#mermaid-svg-1Un3U0ZiDa95gD7P .cluster span{color:#333;}#mermaid-svg-1Un3U0ZiDa95gD7P .nodeLabel,#mermaid-svg-1Un3U0ZiDa95gD7P .edgeLabel{color:#131300;}#mermaid-svg-1Un3U0ZiDa95gD7P .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-1Un3U0ZiDa95gD7P .label text{fill:#131300;}#mermaid-svg-1Un3U0ZiDa95gD7P .labelBkg{background:#ECECFF;}#mermaid-svg-1Un3U0ZiDa95gD7P .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-1Un3U0ZiDa95gD7P .classTitle{font-weight:bolder;}#mermaid-svg-1Un3U0ZiDa95gD7P .node rect,#mermaid-svg-1Un3U0ZiDa95gD7P .node circle,#mermaid-svg-1Un3U0ZiDa95gD7P .node ellipse,#mermaid-svg-1Un3U0ZiDa95gD7P .node polygon,#mermaid-svg-1Un3U0ZiDa95gD7P .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1Un3U0ZiDa95gD7P .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P g.clickable{cursor:pointer;}#mermaid-svg-1Un3U0ZiDa95gD7P g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-1Un3U0ZiDa95gD7P g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-1Un3U0ZiDa95gD7P .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-1Un3U0ZiDa95gD7P .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-1Un3U0ZiDa95gD7P .dashed-line{stroke-dasharray:3;}#mermaid-svg-1Un3U0ZiDa95gD7P .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-1Un3U0ZiDa95gD7P #compositionStart,#mermaid-svg-1Un3U0ZiDa95gD7P .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #compositionEnd,#mermaid-svg-1Un3U0ZiDa95gD7P .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #dependencyStart,#mermaid-svg-1Un3U0ZiDa95gD7P .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #dependencyStart,#mermaid-svg-1Un3U0ZiDa95gD7P .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #extensionStart,#mermaid-svg-1Un3U0ZiDa95gD7P .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #extensionEnd,#mermaid-svg-1Un3U0ZiDa95gD7P .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #aggregationStart,#mermaid-svg-1Un3U0ZiDa95gD7P .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #aggregationEnd,#mermaid-svg-1Un3U0ZiDa95gD7P .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #lollipopStart,#mermaid-svg-1Un3U0ZiDa95gD7P .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P #lollipopEnd,#mermaid-svg-1Un3U0ZiDa95gD7P .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-1Un3U0ZiDa95gD7P .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-1Un3U0ZiDa95gD7P .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1Un3U0ZiDa95gD7P .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1Un3U0ZiDa95gD7P .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1Un3U0ZiDa95gD7P :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} drm_gpuva
xe_vma
drm_gpuva gpuva
xe_userptr_vma
xe_vma vma\nxe_userptr userptr
调用 to_userptr_vma() 前必须先用 xe_vma_is_userptr() 确认形态(函数里有 xe_assert 兜底),否则 container_of 会得到非法指针。
7. 本篇小结
| 机制 | 作用 | 代表符号 |
|---|---|---|
| 结构内嵌(首成员) | 让 Xe 类型"是一个" DRM 核心类型 | xe_vm.gpuvm / xe_vma.gpuva |
container_of 转型 |
在核心层回调边界还原子类型 | gpuvm_to_vm / gpuva_to_vma / gpuva_op_to_vma_op |
| 字段访问器 | 屏蔽父结构布局,便于演进 | xe_vma_start/size/end/bo |
USERBITS 复用 |
私有 VMA 状态零额外内存 | XE_VMA_READ_ONLY ... XE_VMA_SYSTEM_ALLOCATOR |
| 多级继承 | userptr 在 vma 上再扩展 | xe_userptr_vma / to_userptr_vma |
理解了这套"继承 + 转型 + flags 复用"的骨架,后续所有回调和 bind 流程读起来都会顺畅------每当核心层给你一个 drm_* 指针,你就知道该用哪个 container_of 把它变回 xe_*。
关键源码位置速查
| 主题 | 文件 | 关键符号 |
|---|---|---|
| 三对结构定义 | drivers/gpu/drm/xe/xe_vm_types.h |
xe_vm / xe_vma / xe_vma_op |
| 转型 & 访问器 | drivers/gpu/drm/xe/xe_vm.h |
gpuva_to_vma / xe_vma_start ... |
| 父结构定义 | include/drm/drm_gpuvm.h |
drm_gpuva / drm_gpuva_flags |
| 私有 flags | drivers/gpu/drm/xe/xe_vm_types.h |
XE_VMA_*(L41--L52) |
| userptr 子类 | drivers/gpu/drm/xe/xe_vm_types.h |
xe_userptr_vma |
下一篇 :第 3 篇《VM 的创建与销毁》------xe_vm_create() 如何一步步调用 drm_gpuvm_init(),DRM_GPUVM_RESV_PROTECTED 与共享 resv 对象的意义,根页表 / scratch page 的建立,以及引用计数归零后 vm_free 回调与异步销毁 worker 的配合。