02 Xe GPUVM 数据结构与“继承“模型

系列: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_vmdrm_gpuvm

gpuvmxe_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_vmadrm_gpuva

xe_vm_types.h

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_opdrm_gpuva_op

xe_vm_types.h

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.hxe_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 的配合。