DRM 内存管理

现代 Linux 系统需要大量的图形内存来存储帧缓冲区、纹理、顶点和其他图形相关数据。鉴于许多此类数据的动态特性,有效地管理图形内存对于图形堆栈至关重要,并在 DRM 基础设施中发挥核心作用。

DRM 核心包括两个内存管理器,即转换表管理器 (TTM) 和图形执行管理器 (GEM)。TTM 是第一个开发的 DRM 内存管理器,并试图成为一个适用于所有情况的解决方案。它提供了一个单一的用户空间 API,以满足所有硬件的需求,支持统一内存架构 (UMA) 设备和具有专用视频 RAM 的设备(即大多数独立显卡)。这导致了一个庞大而复杂的代码,事实证明对于驱动程序开发来说很难使用。

GEM 最初是英特尔赞助的项目,以应对 TTM 的复杂性。它的设计理念完全不同:GEM 没有为每个与图形内存相关的问题提供解决方案,而是识别了驱动程序之间的公共代码,并创建了一个支持库来共享它。GEM 的初始化和执行要求比 TTM 简单,但没有视频 RAM 管理功能,因此仅限于 UMA 设备。

转换表管理器 (TTM)

TTM 是用于具有专用内存的加速器设备的内存管理器。

基本思想是将资源分组到特定大小的缓冲区对象中,TTM 处理这些对象的生命周期、移动和 CPU 映射。

TODO:在此处添加更多设计背景和信息。

enum ttm_caching

CPU 缓存和总线窥探行为。

常量

ttm_uncached

设备映射的最具防御性的选项,甚至不允许写入合并。

ttm_write_combined

不缓存读取访问,但允许至少写入合并。

ttm_cached

像普通系统内存一样完全缓存,要求设备在访问时窥探 CPU 缓存。

TTM 设备对象引用

struct ttm_global

缓冲区对象驱动程序的全局数据。

定义:

struct ttm_global {
    struct page *dummy_read_page;
    struct list_head device_list;
    atomic_t bo_count;
};

成员

dummy_read_page

指向用于映射未填充页面的虚拟页面的指针。初始化后恒定。

device_list

缓冲区对象设备的列表。受 ttm_global_mutex 保护。

bo_count

设备分配的缓冲区对象的数量。

struct ttm_device

缓冲区对象驱动程序的特定于设备的数据。

定义:

struct ttm_device {
    struct list_head device_list;
    const struct ttm_device_funcs *funcs;
    struct ttm_resource_manager sysman;
    struct ttm_resource_manager *man_drv[TTM_NUM_MEM_TYPES];
    struct drm_vma_offset_manager *vma_manager;
    struct ttm_pool pool;
    spinlock_t lru_lock;
    struct list_head unevictable;
    struct address_space *dev_mapping;
    struct workqueue_struct *wq;
};

成员

device_list

我们在全局设备列表中的条目。在 bo 设备初始化后恒定

funcs

设备的函数表。在 bo 设备初始化后恒定

sysman

系统域的资源管理器。通过 ttm_manager_type 访问。

man_drv

resource_managers 的数组,每个资源类型一个。

vma_manager

用于查找要 mmap 的 BO 的地址空间管理器。

pool

设备的页面池。

lru_lock

保护每个管理器 LRU 和 ddestroy 列表。

unevictable

已固定或交换的缓冲区对象,因此不在 LRU 列表中。

dev_mapping

指向 struct address_space 的指针,用于在缓冲区移动时使 CPU 映射无效。受加载/卸载同步保护。

wq

用于延迟删除工作队列的工作队列结构。

int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *funcs, struct device *dev, struct address_space *mapping, struct drm_vma_offset_manager *vma_manager, bool use_dma_alloc, bool use_dma32)

参数

struct ttm_device *bdev

指向要初始化的 struct ttm_device 的指针。

const struct ttm_device_funcs *funcs

设备的函数表。

struct device *dev

用于 DMA 映射和分配的核心内核设备指针。

struct address_space *mapping

用于此 bo 的地址空间。

struct drm_vma_offset_manager *vma_manager

指向 vma 管理器的指针。

bool use_dma_alloc

是否应使用一致的 DMA 分配 API。

bool use_dma32

是否应为设备内存分配使用 GFP_DMA32。

描述

初始化 struct ttm_device

返回

!0:失败。

TTM 资源放置引用

struct ttm_place

定义:

struct ttm_place {
    unsigned fpfn;
    unsigned lpfn;
    uint32_t mem_type;
    uint32_t flags;
};

成员

fpfn

放置对象的第一个有效页帧号

lpfn

放置对象的最后一个有效页帧号

mem_type

应从中分配资源的 TTM_PL_* 之一。

flags

对象的内存域和缓存标志

描述

指示放置对象的可能位置的结构。

struct ttm_placement

定义:

struct ttm_placement {
    unsigned num_placement;
    const struct ttm_place  *placement;
};

成员

num_placement

首选放置的数量

placement

首选放置

描述

指示您为对象请求的放置的结构。

TTM 资源对象引用

enum ttm_lru_item_type

枚举 ttm_lru_item 子类

常量

TTM_LRU_RESOURCE

资源子类

TTM_LRU_HITCH

迭代器挂钩子类

struct ttm_lru_item

TTM lru 列表节点基类

定义:

struct ttm_lru_item {
    struct list_head link;
    enum ttm_lru_item_type type;
};

成员

link

列表链接

type

子类类型

void ttm_lru_item_init(struct ttm_lru_item *item, enum ttm_lru_item_type type)

初始化 struct ttm_lru_item

参数

struct ttm_lru_item *item

要初始化的项

enum ttm_lru_item_type type

子类类型

struct ttm_resource_manager

定义:

struct ttm_resource_manager {
    bool use_type;
    bool use_tt;
    struct ttm_device *bdev;
    uint64_t size;
    const struct ttm_resource_manager_func *func;
    spinlock_t move_lock;
    struct dma_fence *move;
    struct list_head lru[TTM_MAX_BO_PRIORITY];
    uint64_t usage;
};

成员

use_type

启用的内存类型。

use_tt

是否应将 TT 对象用于后备存储。

bdev

此管理器所属的 ttm 设备

size

托管区域的大小。

func

实现范围管理器的结构指针。请参见上文

move_lock

用于移动栅栏的锁

move

上次流水线移动操作的栅栏。

lru

此内存类型的 lru 列表。

usage

使用了多少资源,受 bdev->lru_lock 保护。

描述

此结构用于标识和管理设备的内存类型。

struct ttm_bus_placement

定义:

struct ttm_bus_placement {
    void *addr;
    phys_addr_t offset;
    bool is_iomem;
    enum ttm_caching        caching;
};

成员

addr

映射的虚拟地址

offset

物理地址

is_iomem

这是 io 内存吗?

caching

请参见 enum ttm_caching

描述

指示对象的总线放置的结构。

struct ttm_resource

定义:

struct ttm_resource {
    unsigned long start;
    size_t size;
    uint32_t mem_type;
    uint32_t placement;
    struct ttm_bus_placement bus;
    struct ttm_buffer_object *bo;
    struct ttm_lru_item lru;
};

成员

start

分配的起始位置。

size

资源实际大小,以字节为单位。

mem_type

分配的资源类型。

placement

放置标志。

bus

放置在 CPU 可访问的 IO 总线上

bo

对 BO 的弱引用,受 ttm_device::lru_lock 保护

lru

最近最少使用列表,请参阅 ttm_resource_manager.lru

描述

指示缓冲区对象使用的放置和空间资源的结构。

struct ttm_resource *ttm_lru_item_to_res(struct ttm_lru_item *item)

struct ttm_lru_item 向下转换为 struct ttm_resource

参数

struct ttm_lru_item *item

要向下转换的 struct ttm_lru_item

返回

指向嵌入的 struct ttm_resource 的指针

struct ttm_lru_bulk_move_pos

定义:

struct ttm_lru_bulk_move_pos {
    struct ttm_resource *first;
    struct ttm_resource *last;
};

成员

first

批量移动范围内的第一个资源

last

批量移动范围内的最后一个资源

描述

用于 LRU 批量移动的资源范围。

struct ttm_lru_bulk_move

定义:

struct ttm_lru_bulk_move {
    struct ttm_lru_bulk_move_pos pos[TTM_NUM_MEM_TYPES][TTM_MAX_BO_PRIORITY];
    struct list_head cursor_list;
};

成员

pos

每个域/优先级中资源的第一个/最后一个 LRU 条目

cursor_list

当前正在遍历 **pos** 的任何子列表的游标列表。受 ttm 设备 lru_lock 保护。

描述

当前批量移动状态的容器。应与 ttm_lru_bulk_move_init() 和 ttm_bo_set_bulk_move() 一起使用。批量移动结构中的所有 BO 都需要共享同一个预留对象,以确保即使仅逐出批量中的一个 BO,整个批量也会被锁定以进行逐出。

struct ttm_resource_cursor

定义:

struct ttm_resource_cursor {
    struct ttm_resource_manager *man;
    struct ttm_lru_item hitch;
    struct list_head bulk_link;
    struct ttm_lru_bulk_move *bulk;
    unsigned int mem_type;
    unsigned int priority;
};

成员

man

当前正在迭代的资源管理器

hitch

插入到要迭代的下一个资源之前的挂接列表节点。

bulk_link

用于遍历 **bulk** 的批量子列表的游标列表的列表链接。受 ttm 设备 lru_lock 保护。

bulk

指向 struct ttm_lru_bulk_move 的指针,**hitch** 插入到该指针的子范围内。如果为 NULL,则表示没有。永远不要取消对此指针的引用,因为指向的 struct ttm_lru_bulk_move 对象可能已被释放。该指针仅用于比较。

mem_type

正在遍历的 LRU 列表的内存类型。当 **bulk** != NULL 时,此字段有效。

priority

当前优先级

描述

用于迭代管理器中资源的游标。

struct ttm_kmap_iter_iomap

针对由 struct io_mapping + struct sg_table 支持的 struct ttm_resource 的专门化。

定义:

struct ttm_kmap_iter_iomap {
    struct ttm_kmap_iter base;
    struct io_mapping *iomap;
    struct sg_table *st;
    resource_size_t start;
    struct {
        struct scatterlist *sg;
        pgoff_t i;
        pgoff_t end;
        pgoff_t offs;
    } cache;
};

成员

base

嵌入的 struct ttm_kmap_iter,提供使用接口。

iomap

表示底层线性 io_memory 的 struct io_mapping。

st

进入 **iomap** 的 sg_table,表示 struct ttm_resource 的内存。

start

需要从 **st** 中减去的偏移量,以使 sg_dma_address(st->sgl) - **start** == 0 用于 **iomap** 的起始位置。

cache

用于快速查找的散列表遍历缓存。

cache.sg

指向当前缓存的散列表段的指针。

cache.i

**sg** 的第一个索引。PAGE_SIZE 粒度。

cache.end

**sg** 的最后一个索引 + 1。PAGE_SIZE 粒度。

cache.offs

**sg** 的 **iomap** 的第一个偏移量。PAGE_SIZE 粒度。

struct ttm_kmap_iter_linear_io

用于线性 IO 的迭代器专门化

定义:

struct ttm_kmap_iter_linear_io {
    struct ttm_kmap_iter base;
    struct iosys_map dmap;
    bool needs_unmap;
};

成员

base

基本迭代器

dmap

指向区域起始地址

needs_unmap

是否需要在 fini 上取消映射

void ttm_resource_manager_set_used(struct ttm_resource_manager *man, bool used)

参数

struct ttm_resource_manager *man

内存管理器对象。

bool used

要设置的使用状态。

描述

设置管理器使用标志。如果禁用,则不再将管理器用于对象放置。

bool ttm_resource_manager_used(struct ttm_resource_manager *man)

参数

struct ttm_resource_manager *man

要获取使用状态的管理器

描述

获取管理器的使用中标志。

返回

true 为使用中,false 为未使用。

void ttm_resource_manager_cleanup(struct ttm_resource_manager *man)

参数

struct ttm_resource_manager *man

内存管理器对象。

描述

从内存管理器对象中清理移动栅栏。

ttm_resource_manager_for_each_res

ttm_resource_manager_for_each_res (man, cursor, res)

迭代所有资源

参数

man

资源管理器

cursor

当前位置的 struct ttm_resource_cursor

res

当前资源

描述

迭代资源管理器中所有可逐出的资源。

void ttm_lru_bulk_move_init(struct ttm_lru_bulk_move *bulk)

初始化批量移动结构

参数

struct ttm_lru_bulk_move *bulk

要初始化的结构

描述

现在只需将结构 memset 为零。

void ttm_lru_bulk_move_fini(struct ttm_device *bdev, struct ttm_lru_bulk_move *bulk)

完成批量移动结构

参数

struct ttm_device *bdev

The struct ttm_device

struct ttm_lru_bulk_move *bulk

用于最终化的结构体

描述

健全性检查,确保批量移动没有剩余任何资源,因此没有附加任何游标。

void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk)

将资源批量移动到 LRU 尾部。

参数

struct ttm_lru_bulk_move *bulk

批量移动结构体

描述

将 BO 批量移动到 LRU 尾部,仅当驱动程序确保资源顺序永远不会更改时才有效。应该在持有 ttm_device.lru_lock 的情况下调用。

void ttm_resource_init(struct ttm_buffer_object *bo, const struct ttm_place *place, struct ttm_resource *res)

资源对象构造函数

参数

struct ttm_buffer_object *bo

为此资源分配的缓冲区对象

const struct ttm_place *place

资源的位置

struct ttm_resource *res

要初始化的资源对象

描述

初始化一个新的资源对象。对应于 ttm_resource_fini()

void ttm_resource_fini(struct ttm_resource_manager *man, struct ttm_resource *res)

资源析构函数

参数

struct ttm_resource_manager *man

此资源所属的资源管理器

struct ttm_resource *res

要清理的资源

描述

应该由资源管理器后端使用,在释放底层结构之前清理 TTM 资源对象。确保在销毁之前从 LRU 中移除资源。对应于 ttm_resource_init()

void ttm_resource_manager_init(struct ttm_resource_manager *man, struct ttm_device *bdev, uint64_t size)

参数

struct ttm_resource_manager *man

要初始化的内存管理器对象

struct ttm_device *bdev

此管理器所属的 ttm 设备

uint64_t size

以任意单位表示的托管资源大小

描述

初始化管理器对象的核心部分。

uint64_t ttm_resource_manager_usage(struct ttm_resource_manager *man)

参数

struct ttm_resource_manager *man

内存管理器对象。

描述

返回当前使用了多少资源。

void ttm_resource_manager_debug(struct ttm_resource_manager *man, struct drm_printer *p)

参数

struct ttm_resource_manager *man

要转储的管理器类型。

struct drm_printer *p

用于调试的打印机。

struct ttm_kmap_iter *ttm_kmap_iter_iomap_init(struct ttm_kmap_iter_iomap *iter_io, struct io_mapping *iomap, struct sg_table *st, resource_size_t start)

初始化 struct ttm_kmap_iter_iomap

参数

struct ttm_kmap_iter_iomap *iter_io

要初始化的 struct ttm_kmap_iter_iomap

struct io_mapping *iomap

表示底层线性 io_memory 的 struct io_mapping。

struct sg_table *st

进入 **iomap** 的 sg_table,表示 struct ttm_resource 的内存。

resource_size_t start

需要从 **st** 中减去的偏移量,以使 sg_dma_address(st->sgl) - **start** == 0 用于 **iomap** 的起始位置。

返回

指向嵌入式 struct ttm_kmap_iter 的指针。

void ttm_resource_manager_create_debugfs(struct ttm_resource_manager *man, struct dentry *parent, const char *name)

为指定的资源管理器创建 debugfs 条目。

参数

struct ttm_resource_manager *man

为其创建 debugfs 统计文件的 TTM 资源管理器

struct dentry * parent

文件所在的 debugfs 目录

const char *name

要创建的文件名。

描述

此函数设置一个 debugfs 文件,可用于查看指定的 ttm_resource_manager 的调试统计信息。

TTM TT 对象引用

struct ttm_tt

这是一个结构体,用于保存非由固定(VRAM/AGP)内存支持的缓冲区对象的页面、缓存和光圈绑定状态。

定义:

struct ttm_tt {
    struct page **pages;
#define TTM_TT_FLAG_SWAPPED             BIT(0);
#define TTM_TT_FLAG_ZERO_ALLOC          BIT(1);
#define TTM_TT_FLAG_EXTERNAL            BIT(2);
#define TTM_TT_FLAG_EXTERNAL_MAPPABLE   BIT(3);
#define TTM_TT_FLAG_DECRYPTED           BIT(4);
#define TTM_TT_FLAG_PRIV_POPULATED      BIT(5);
    uint32_t page_flags;
    uint32_t num_pages;
    struct sg_table *sg;
    dma_addr_t *dma_address;
    struct file *swap_storage;
    enum ttm_caching caching;
};

成员

pages

支持数据的页面数组。

page_flags

页面标志。

支持的值

TTM_TT_FLAG_SWAPPED: 由 TTM 设置,当页面已被取消填充并由 TTM 换出时。调用 ttm_tt_populate() 将把页面换回,并取消设置该标志。驱动程序通常永远不需要接触此标志。

TTM_TT_FLAG_ZERO_ALLOC:如果页面将在分配时清零,则设置此标志。

TTM_TT_FLAG_EXTERNAL:如果底层页面是外部分配的,例如使用 dma-buf 或 userptr,则设置此标志。这实际上禁用了 TTM 换出此类页面。同样重要的是防止 TTM 直接映射这些页面。

请注意,枚举 ttm_bo_type.ttm_bo_type_sg 对象将始终启用此标志。

TTM_TT_FLAG_EXTERNAL_MAPPABLE:与 TTM_TT_FLAG_EXTERNAL 相同的行为,但减少了限制,仍然可以使用 TTM 直接映射页面。当实现一个仍然在底层分配驱动程序拥有的页面(例如使用 shmem)的 ttm_tt 后端时,这很有用。

请注意,由于这也暗示了 TTM_TT_FLAG_EXTERNAL,因此此处的用法应始终为

page_flags = TTM_TT_FLAG_EXTERNAL |

TTM_TT_FLAG_EXTERNAL_MAPPABLE;

TTM_TT_FLAG_DECRYPTED:映射的 ttm 页面应标记为未加密。框架将尝试匹配 dma 层正在执行的操作,但请注意,它有点脆弱,因为 ttm 页面错误处理有点滥用了 DMA api,并且 dma_map_attrs 不能用于确保 pgprot 始终匹配。

TTM_TT_FLAG_PRIV_POPULATED:仅限 TTM 内部使用。请勿使用。这由 TTM 在 ttm_tt_populate() 成功返回后设置,然后在 TTM 调用 ttm_tt_unpopulate() 时取消设置。

num_pages

页面数组中的页面数。

sg

用于通过 dma-buf 进行 SG 对象。

dma_address

页面的 DMA(总线)地址。

swap_storage

指向用于交换存储的 shmem struct file 的指针。

caching

页面的当前缓存状态,请参阅 enum ttm_caching

struct ttm_kmap_iter_tt

用于 tt 的映射迭代器的特殊化。

定义:

struct ttm_kmap_iter_tt {
    struct ttm_kmap_iter base;
    struct ttm_tt *tt;
    pgprot_t prot;
};

成员

base

嵌入的 struct ttm_kmap_iter,提供使用接口

tt

缓存的 struct ttm_tt

prot

用于映射的缓存页面保护。

int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc)

参数

struct ttm_buffer_object *bo

指向 struct ttm_buffer_object 的指针

bool zero_alloc

如果分配的页面需要清零,则为 true

描述

确保为给定的 BO 分配了 TTM 结构。实际上没有分配任何页面。

int ttm_tt_init(struct ttm_tt *ttm, struct ttm_buffer_object *bo, uint32_t page_flags, enum ttm_caching caching, unsigned long extra_pages)

参数

struct ttm_tt *ttm

struct ttm_tt

struct ttm_buffer_object *bo

我们为其创建 ttm 的缓冲区对象。

uint32_t page_flags

由 TTM_TT_FLAG_XX 标志标识的页面标志。

enum ttm_caching caching

页面的所需缓存状态

unsigned long extra_pages

驱动程序所需的额外页面。

描述

创建一个 struct ttm_tt,用于使用系统内存页面备份数据。实际上没有分配任何页面。

返回

NULL:内存不足。

void ttm_tt_fini(struct ttm_tt *ttm)

参数

struct ttm_tt *ttm

ttm_tt 结构。

描述

释放 ttm_tt 结构的内存

void ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm)

参数

struct ttm_device *bdev

此对象所属的 ttm_device

struct ttm_tt *ttm

struct ttm_tt

描述

取消绑定、取消填充并销毁通用的 struct ttm_tt

int ttm_tt_swapin(struct ttm_tt *ttm)

参数

struct ttm_tt *ttm

struct ttm_tt

描述

换入先前换出的 ttm_tt。

int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)

为 ttm 分配页面

参数

struct ttm_device *bdev

此对象所属的 ttm_device

struct ttm_tt *ttm

指向 ttm_tt 结构的指针

struct ttm_operation_ctx *ctx

用于填充 tt 对象的操作上下文。

描述

调用驱动程序方法为 ttm 分配页面

void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm)

从 ttm 释放页面

参数

struct ttm_device *bdev

此对象所属的 ttm_device

struct ttm_tt *ttm

指向 ttm_tt 结构的指针

描述

调用驱动程序方法释放 ttm 的所有页面

void ttm_tt_mark_for_clear(struct ttm_tt *ttm)

标记页面以便在填充时清除。

参数

struct ttm_tt *ttm

指向 ttm_tt 结构的指针

描述

标记页面以便清除,以便下次填充页面向量时,页面将被清除。

struct ttm_tt *ttm_agp_tt_create(struct ttm_buffer_object *bo, struct agp_bridge_data *bridge, uint32_t page_flags)

参数

struct ttm_buffer_object *bo

我们为其分配 ttm 的缓冲区对象。

struct agp_bridge_data *bridge

此设备所连接的 agp 桥。

uint32_t page_flags

由 TTM_TT_FLAG_XX 标志标识的页面标志。

描述

创建一个 TTM 后端,该后端使用指示的 AGP 桥作为 TT 内存的孔径。此函数使用 linux agpgart 接口来绑定和解绑支持 ttm_tt 的内存。

struct ttm_kmap_iter *ttm_kmap_iter_tt_init(struct ttm_kmap_iter_tt *iter_tt, struct ttm_tt *tt)

初始化 struct ttm_kmap_iter_tt

参数

struct ttm_kmap_iter_tt *iter_tt

要初始化的 struct ttm_kmap_iter_tt

struct ttm_tt *tt

保存 struct ttm_resource 的页面指针的 Struct ttm_tt。

返回

指向嵌入式 struct ttm_kmap_iter 的指针。

TTM 页面池参考

struct ttm_pool_type

特定内存类型的池

定义:

struct ttm_pool_type {
    struct ttm_pool *pool;
    unsigned int order;
    enum ttm_caching caching;
    struct list_head shrinker_list;
    spinlock_t lock;
    struct list_head pages;
};

成员

pool

我们所属的池,全局池可能为 NULL

阶数

我们页面分配的阶数

caching

我们页面的缓存类型

shrinker_list

我们在全局收缩器列表中的位置

页面列表的保护

pages

池中的页面列表

struct ttm_pool

用于所有缓存和阶数的池

定义:

struct ttm_pool {
    struct device *dev;
    int nid;
    bool use_dma_alloc;
    bool use_dma32;
    struct {
        struct ttm_pool_type orders[NR_PAGE_ORDERS];
    } caching[TTM_NUM_CACHING_TYPES];
};

成员

dev

我们为其分配页面的设备

nid

要使用的 NUMA 节点

use_dma_alloc

是否应使用一致的 DMA 分配

use_dma32

是否应使用 GFP_DMA32

caching

每个缓存/阶数的池

int ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt, struct ttm_operation_ctx *ctx)

填充一个 ttm_tt 对象

参数

struct ttm_pool *pool

要使用的 ttm_pool

struct ttm_tt *tt

要填充的 ttm_tt 对象

struct ttm_operation_ctx *ctx

操作上下文

描述

使用页面填充 ttm_tt 对象,并确保在必要时进行 DMA 映射。

返回

成功时为 0,否则为负错误代码。

void ttm_pool_free(struct ttm_pool *pool, struct ttm_tt *tt)

从 ttm_tt 对象释放后备页面

参数

struct ttm_pool *pool

将页面返回到的池。

struct ttm_tt *tt

要取消填充的 ttm_tt 对象

描述

将后备页面返回到池或释放它们

void ttm_pool_init(struct ttm_pool *pool, struct device *dev, int nid, bool use_dma_alloc, bool use_dma32)

初始化一个池

参数

struct ttm_pool *pool

要初始化的池

struct device *dev

用于 DMA 分配和映射的设备

int nid

用于分配的 NUMA 节点

bool use_dma_alloc

如果应使用一致的 DMA 分配,则为 true

bool use_dma32

如果应使用 GFP_DMA32,则为 true

描述

初始化池及其池类型。

void ttm_pool_fini(struct ttm_pool *pool)

清理一个池

参数

struct ttm_pool *pool

要清理的池

描述

释放池中的所有页面,并从全局收缩器中注销类型。

int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m)

池的 Debugfs 转储函数

参数

struct ttm_pool *pool

要为其转储信息的池

struct seq_file *m

要转储到的 seq_file

描述

使用每个池和全局信息进行 debugfs 转储。

图形执行管理器 (GEM)

GEM 设计方法导致内存管理器在其用户空间或内核 API 中未提供所有(甚至所有常见)用例的完整覆盖。 GEM 向用户空间公开一组标准的内存相关操作,并向驱动程序公开一组辅助函数,并让驱动程序使用其自己的私有 API 实现特定于硬件的操作。

GEM 用户空间 API 在 LWN 上的GEM - 图形执行管理器文章中进行了描述。虽然稍微过时,但该文档提供了 GEM API 原则的良好概述。作为通用 GEM API 的一部分描述的缓冲区分配以及读取和写入操作当前是使用特定于驱动程序的 ioctl 实现的。

GEM 与数据无关。它管理抽象的缓冲区对象,而不知道各个缓冲区包含的内容。因此,需要了解缓冲区内容或用途的 API(例如缓冲区分配或同步原语)超出了 GEM 的范围,必须使用特定于驱动程序的 ioctl 实现。

从根本上说,GEM 涉及以下几个操作

  • 内存分配和释放

  • 命令执行

  • 命令执行时的光圈管理

缓冲区对象分配相对简单,并且主要由 Linux 的 shmem 层提供,该层提供内存来支持每个对象。

特定于设备的操作(例如命令执行、锁定、缓冲区读取和写入、映射以及域所有权转移)留给特定于驱动程序的 ioctl。

GEM 初始化

使用 GEM 的驱动程序必须在 struct struct drm_driver driver_features 字段中设置 DRIVER_GEM 位。然后,DRM 核心将在调用加载操作之前自动初始化 GEM 核心。在后台,这将创建一个 DRM 内存管理器对象,该对象为对象分配提供地址空间池。

在 KMS 配置中,如果硬件需要,驱动程序需要在核心 GEM 初始化之后分配和初始化命令环形缓冲区。 UMA 设备通常具有所谓的“被盗”内存区域,该区域为初始帧缓冲区和设备所需的大型连续内存区域提供空间。此空间通常不由 GEM 管理,必须单独初始化到其自己的 DRM MM 对象中。

GEM 对象创建

GEM 将 GEM 对象的创建和支持它们的内存的分配分为两个不同的操作。

GEM 对象由 struct struct drm_gem_object 的实例表示。驱动程序通常需要使用私有信息扩展 GEM 对象,因此创建一个特定于驱动程序的 GEM 对象结构类型,该类型嵌入了 struct struct drm_gem_object 的实例。

要创建 GEM 对象,驱动程序会为其特定的 GEM 对象类型实例分配内存,并通过调用 drm_gem_object_init() 来初始化嵌入的 struct struct drm_gem_object。该函数接收指向 DRM 设备的指针、指向 GEM 对象的指针和缓冲区对象的大小(以字节为单位)。

GEM 使用 shmem 来分配匿名的可分页内存。drm_gem_object_init() 将创建请求大小的 shmfs 文件,并将其存储到 struct struct drm_gem_object filp 字段中。当图形硬件直接使用系统内存时,该内存用作对象的主要存储,否则用作后备存储。

驱动程序负责通过为每个页面调用 shmem_read_mapping_page_gfp() 来进行实际的物理页面分配。 请注意,他们可以决定在初始化 GEM 对象时分配页面,或者将分配延迟到需要内存时(例如,当由于用户空间内存访问或驱动程序需要启动涉及内存的 DMA 传输时发生页面错误)。

并非总是需要匿名的可分页内存,例如,当硬件需要物理上连续的系统内存时,嵌入式设备中通常是这种情况。 驱动程序可以通过调用 drm_gem_private_object_init() 而不是 drm_gem_object_init() 来创建没有 shmfs 后备(称为私有 GEM 对象)的 GEM 对象。私有 GEM 对象的存储必须由驱动程序管理。

GEM 对象生命周期

所有 GEM 对象都由 GEM 核心进行引用计数。可以通过分别调用 drm_gem_object_get()drm_gem_object_put() 来获取和释放引用。

当对 GEM 对象的最后一个引用被释放时,GEM 核心会调用 struct drm_gem_object_funcs 的 free 操作。此操作对于启用 GEM 的驱动程序是强制性的,并且必须释放 GEM 对象和所有相关的资源。

void (*free) (struct drm_gem_object *obj); 驱动程序负责释放所有 GEM 对象资源。这包括由 GEM 核心创建的资源,这些资源需要使用 drm_gem_object_release() 释放。

GEM 对象命名

用户空间和内核之间的通信使用本地句柄、全局名称或最近使用的文件描述符来引用 GEM 对象。所有这些都是 32 位整数值;通常的 Linux 内核限制适用于文件描述符。

GEM 句柄是 DRM 文件本地的。应用程序通过驱动程序特定的 ioctl 获取 GEM 对象的句柄,并且可以使用该句柄在其他标准或驱动程序特定的 ioctl 中引用该 GEM 对象。关闭 DRM 文件句柄会释放其所有 GEM 句柄并取消引用关联的 GEM 对象。

要为 GEM 对象创建句柄,驱动程序会调用 drm_gem_handle_create()。该函数接受指向 DRM 文件和 GEM 对象的指针,并返回一个本地唯一的句柄。当不再需要该句柄时,驱动程序会通过调用 drm_gem_handle_delete() 将其删除。最后,可以通过调用 drm_gem_object_lookup() 来检索与句柄关联的 GEM 对象。

句柄不拥有 GEM 对象的所有权,它们只引用该对象,当句柄被销毁时,该引用将被删除。为了避免泄漏 GEM 对象,驱动程序必须确保在适当的时候删除它们拥有的引用(例如在对象创建时获得的初始引用),而无需特别考虑句柄。例如,在实现 dumb_create 操作中组合 GEM 对象和句柄创建的特定情况下,驱动程序必须在返回句柄之前删除对 GEM 对象的初始引用。

GEM 名称在目的上与句柄相似,但不是 DRM 文件本地的。它们可以在进程之间传递,以全局引用 GEM 对象。名称不能直接用于引用 DRM API 中的对象,应用程序必须分别使用 DRM_IOCTL_GEM_FLINK 和 DRM_IOCTL_GEM_OPEN ioctl 将句柄转换为名称,并将名称转换为句柄。转换由 DRM 核心处理,无需任何驱动程序特定的支持。

GEM 还通过 PRIME 支持使用 dma-buf 文件描述符进行缓冲区共享。基于 GEM 的驱动程序必须使用提供的帮助程序函数来正确实现导出和导入。请参阅?。由于共享文件描述符本质上比容易猜测的全局 GEM 名称更安全,因此它是首选的缓冲区共享机制。通过 GEM 名称共享缓冲区仅支持旧版用户空间。此外,PRIME 还允许跨设备缓冲区共享,因为它基于 dma-bufs。

GEM 对象映射

由于映射操作相当繁重,GEM 倾向于通过驱动程序特定的 ioctl 实现对缓冲区的读/写式访问,而不是将缓冲区映射到用户空间。但是,当需要对缓冲区进行随机访问(例如执行软件渲染)时,直接访问对象可能更有效。

mmap 系统调用不能直接用于映射 GEM 对象,因为它们没有自己的文件句柄。目前存在两种替代方法可以将 GEM 对象映射到用户空间。第一种方法使用驱动程序特定的 ioctl 执行映射操作,在底层调用 do_mmap()。这通常被认为是可疑的,对于新的启用 GEM 的驱动程序似乎不鼓励使用,因此这里不作描述。

第二种方法在 DRM 文件句柄上使用 mmap 系统调用。void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); DRM 通过 mmap offset 参数传递的伪偏移量来标识要映射的 GEM 对象。在映射之前,必须将 GEM 对象与伪偏移量关联。为此,驱动程序必须在对象上调用 drm_gem_create_mmap_offset()

一旦分配,必须以驱动程序特定的方式将伪偏移量值传递给应用程序,然后将其用作 mmap 偏移量参数。

GEM 核心提供了一个帮助程序方法 drm_gem_mmap() 来处理对象映射。该方法可以直接设置为 mmap 文件操作处理程序。它将根据偏移量值查找 GEM 对象,并将 VMA 操作设置为 struct drm_driver gem_vm_ops 字段。请注意,drm_gem_mmap() 不会将内存映射到用户空间,而是依赖于驱动程序提供的故障处理程序来单独映射页面。

要使用 drm_gem_mmap(),驱动程序必须使用指向 VM 操作的指针来填充 struct struct drm_driver gem_vm_ops 字段。

VM 操作是一个 struct vm_operations_struct,由几个字段组成,其中比较有趣的字段是

struct vm_operations_struct {
        void (*open)(struct vm_area_struct * area);
        void (*close)(struct vm_area_struct * area);
        vm_fault_t (*fault)(struct vm_fault *vmf);
};

open 和 close 操作必须更新 GEM 对象引用计数。驱动程序可以直接使用 drm_gem_vm_open()drm_gem_vm_close() 帮助程序函数作为 open 和 close 处理程序。

当发生页面错误时,fault 操作处理程序负责将各个页面映射到用户空间。根据内存分配方案,驱动程序可以在发生错误时分配页面,或者可以决定在创建对象时为 GEM 对象分配内存。

希望预先映射 GEM 对象而不是处理页面错误的驱动程序可以实现它们自己的 mmap 文件操作处理程序。

对于没有 MMU 的平台,GEM 核心提供了一个帮助程序方法 drm_gem_dma_get_unmapped_area()。mmap() 例程将调用此方法来获取建议的映射地址。

要使用 drm_gem_dma_get_unmapped_area(),驱动程序必须使用指向 drm_gem_dma_get_unmapped_area() 的指针来填充 struct struct file_operations get_unmapped_area 字段。

有关 get_unmapped_area 的更详细信息,请参见 No-MMU memory mapping support

内存一致性

当映射到设备或在命令缓冲区中使用时,对象的后备页面将被刷新到内存并标记为写入组合,以便与 GPU 保持一致。同样,如果 CPU 在 GPU 完成渲染对象后访问该对象,则该对象必须与 CPU 的内存视图保持一致,通常涉及各种 GPU 缓存刷新。这种核心 CPU<->GPU 一致性管理由设备特定的 ioctl 提供,该 ioctl 评估对象的当前域,并执行任何必要的刷新或同步,以将对象放入所需的一致性域(请注意,对象可能很忙,即处于活动渲染目标;在这种情况下,设置域会阻止客户端并等待渲染完成,然后再执行任何必要的刷新操作)。

命令执行

对于 GPU 设备,最重要的 GEM 功能可能是为客户端提供命令执行接口。客户端程序构造包含对先前分配的内存对象的引用的命令缓冲区,然后将其提交给 GEM。此时,GEM 会注意将所有对象绑定到 GTT 中,执行缓冲区,并提供客户端访问相同缓冲区之间必要的同步。这通常涉及从 GTT 中逐出一些对象并重新绑定其他对象(这是一个相当昂贵的操作),并提供从客户端隐藏固定 GTT 偏移量的重定位支持。客户端必须注意不要提交引用的对象数量超过 GTT 容量的命令缓冲区;否则,GEM 会拒绝它们,并且不会发生任何渲染。同样,如果缓冲区中的多个对象需要分配栅栏寄存器才能正确渲染(例如,在 965 之前的芯片上进行 2D blit),则必须注意不要需要的栅栏寄存器多于客户端可用的栅栏寄存器。这种资源管理应该在 libdrm 中从客户端抽象出来。

GEM 函数参考

enum drm_gem_object_status

用于 fdinfo 报告的对象状态的位掩码

常量

DRM_GEM_OBJECT_RESIDENT

对象驻留在内存中(即,未取消固定)

DRM_GEM_OBJECT_PURGEABLE

对象被用户空间标记为可清除

描述

用于 fdinfo 内存统计的状态位掩码,请参阅drm_gem_object_funcs.statusdrm_show_fdinfo()。请注意,即使对象仍然处于活动状态或未驻留内存中,也可能被标记为 DRM_GEM_OBJECT_PURGEABLE,在这种情况下,drm_show_fdinfo() 将不会将其计为可清除的。因此,驱动程序无需检查缓冲区是否空闲且驻留在内存中以返回此位。(即,即使缓冲区仍在 GPU 上处于繁忙状态,用户空间也可以将其标记为可清除的... 它只有在空闲时才会 _真正_ 变为可清除的。状态 gem 对象函数无需考虑这一点。)

struct drm_gem_object_funcs

GEM 对象函数

定义:

struct drm_gem_object_funcs {
    void (*free)(struct drm_gem_object *obj);
    int (*open)(struct drm_gem_object *obj, struct drm_file *file);
    void (*close)(struct drm_gem_object *obj, struct drm_file *file);
    void (*print_info)(struct drm_printer *p, unsigned int indent, const struct drm_gem_object *obj);
    struct dma_buf *(*export)(struct drm_gem_object *obj, int flags);
    int (*pin)(struct drm_gem_object *obj);
    void (*unpin)(struct drm_gem_object *obj);
    struct sg_table *(*get_sg_table)(struct drm_gem_object *obj);
    int (*vmap)(struct drm_gem_object *obj, struct iosys_map *map);
    void (*vunmap)(struct drm_gem_object *obj, struct iosys_map *map);
    int (*mmap)(struct drm_gem_object *obj, struct vm_area_struct *vma);
    int (*evict)(struct drm_gem_object *obj);
    enum drm_gem_object_status (*status)(struct drm_gem_object *obj);
    size_t (*rss)(struct drm_gem_object *obj);
    const struct vm_operations_struct *vm_ops;
};

成员

free

drm_gem_objects 的析构函数。

此回调是必需的。

open

在创建 GEM 句柄时调用。

此回调是可选的。

close

在释放 GEM 句柄时调用。

此回调是可选的。

print_info

如果驱动程序子类化 drm_gem_object 结构,则可以实现此可选挂钩以打印额外的驱动程序特定信息。

应在回调中使用 drm_printf_indent(),并传入 indent 参数。

此回调从 drm_gem_print_info() 调用。

此回调是可选的。

export

将后备缓冲区导出为 dma_buf。如果未设置此项,则使用 drm_gem_prime_export()

此回调是可选的。

pin

将后备缓冲区固定在内存中。由 drm_gem_map_attach() 助手使用。

此回调是可选的。

unpin

取消固定后备缓冲区。由 drm_gem_map_detach() 助手使用。

此回调是可选的。

get_sg_table

返回缓冲区的散列表表示。在通过 drm_gem_map_dma_buf() 助手导出缓冲区时使用。释放通过在 drm_gem_unmap_buf() 中调用 dma_unmap_sg_attrs() 和 sg_free_table() 完成,因此这些助手和此回调不能用于指向驱动程序私有内存范围的散列表。

另请参阅 drm_prime_pages_to_sg()

vmap

返回缓冲区的虚拟地址。由 drm_gem_dmabuf_vmap() 助手使用。

此回调是可选的。

vunmap

释放先前由 vmap 返回的地址。由 drm_gem_dmabuf_vunmap() 助手使用。

此回调是可选的。

mmap

处理 gem 对象的 mmap(),相应地设置 vma。

此回调是可选的。

此回调由 drm_gem_mmap_obj()drm_gem_prime_mmap() 使用。当存在 mmap 时,不使用 vm_opsmmap 回调必须设置 vma->vm_ops。

evict

从内存中驱逐 gem 对象。由 drm_gem_object_evict() 助手使用。成功时返回 0,否则返回 -errno。

此回调是可选的。

status

可选的状态回调可以返回额外的对象状态,该状态确定对象计入哪些统计信息。此回调在 table_lock 下调用。“与对象状态更改竞争”是“无害的”,并且回调可以预期不会与对象销毁竞争。

drm_show_memory_stats() 调用。

rss

返回对象在物理内存中的驻留大小。

drm_show_memory_stats() 调用。

vm_ops

与 mmap 一起使用的虚拟内存操作。

这是可选的,但对于 mmap 支持是必需的。

struct drm_gem_lru

一个简单的 LRU 助手

定义:

struct drm_gem_lru {
    struct mutex *lock;
    long count;
    struct list_head list;
};

成员

锁定保护 GEM 对象在 LRU 之间移动。对象可以在其间移动的所有 LRU 都应受到同一锁的保护。

count

此 LRU 中 GEM 对象的后备页总数。

list

LRU 列表。

描述

用于跟踪给定状态下的 GEM 对象的助手,以帮助驱动程序的收缩器实现。跟踪用于无锁 shrinker.count_objects 的页面计数,并为驱动程序的 shrinker.scan_objects 实现提供 drm_gem_lru_scan

struct drm_gem_object

GEM 缓冲区对象

定义:

struct drm_gem_object {
    struct kref refcount;
    unsigned handle_count;
    struct drm_device *dev;
    struct file *filp;
    struct drm_vma_offset_node vma_node;
    size_t size;
    int name;
    struct dma_buf *dma_buf;
    struct dma_buf_attachment *import_attach;
    struct dma_resv *resv;
    struct dma_resv _resv;
    struct {
        struct list_head list;
#ifdef CONFIG_LOCKDEP;
        struct lockdep_map *lock_dep_map;
#endif;
    } gpuva;
    const struct drm_gem_object_funcs *funcs;
    struct list_head lru_node;
    struct drm_gem_lru *lru;
};

成员

refcount

此对象的引用计数

请使用 drm_gem_object_get() 获取,并使用 drm_gem_object_put_locked() 或 drm_gem_object_put() 释放对 GEM 缓冲区对象的引用。

handle_count

这是此对象的 GEM file_priv 句柄计数。

每个句柄也保持一个引用。请注意,当 handle_count 降至 0 时,将清除任何全局名称(例如,flink 命名空间中的 id)。

drm_device.object_name_lock 保护。

dev

此对象所属的 DRM 设备。

filp

用作可交换缓冲区对象的后备存储的 SHMEM 文件节点。GEM 还支持具有驱动程序特定后备存储(连续 DMA 内存、特殊保留块)的驱动程序私有对象。在这种情况下,filp 为 NULL。

vma_node

此对象的映射信息以支持 mmap。驱动程序应使用 drm_gem_create_mmap_offset() 分配 mmap 偏移量。可以使用 drm_vma_node_offset_addr() 检索偏移量本身。

内存映射本身由 drm_gem_mmap() 处理,它还会检查是否允许用户空间访问该对象。

size

对象的大小(以字节为单位)。在对象的生命周期内不可变。

name

此对象的全局名称,从 1 开始。0 表示未命名。访问由 drm_device.object_name_lock 覆盖。这由 GEM_FLINK 和 GEM_OPEN ioctl 使用。

dma_buf

与此 GEM 对象关联的 dma-buf。

指向与此 gem 对象关联的 dma-buf 的指针(通过导入或导出)。当此对象的最后一个 gem 句柄被释放时,我们会打破生成的引用循环。

drm_device.object_name_lock 保护。

import_attach

支持此对象的 dma-buf 附件。

作为 gem 对象导入的任何外部 dma_buf 都将此设置为设备的附件点。这在 gem 对象的生命周期内是不变的。

drm_gem_object_funcs.free 回调负责清理 dma_buf 附件和导入时获取的引用。

请注意,drm gem/prime 核心不再依赖于驱动程序设置此字段。因此,对于此字段没有意义的驱动程序(例如,虚拟设备或 usb 总线后面的 displaylink),它们可以简单地将其保留为 NULL。

resv

指向与此 GEM 对象关联的预留对象的指针。

通常 (resv == &**_resv**),除了导入的 GEM 对象。

_resv

此 GEM 对象的预留对象。

这对于导入的 GEM 对象未使用。

gpuva

提供附加到此 GEM 对象的 GPU VA 列表。

驱动程序应使用 GEM 的 dma_resv 锁 (drm_gem_object.resv) 或自定义锁(如果提供了)来锁定列表访问。

funcs

可选的 GEM 对象函数。如果设置了此项,它将代替相应的 drm_driver GEM 回调。

新驱动程序应使用此项。

lru_node

drm_gem_lru 中的列表节点。

lru

GEM 对象所在的当前 LRU 列表。

描述

此结构定义了 GEM 缓冲区对象的通用部分,这些部分主要围绕处理 mmap 和用户空间句柄。

缓冲区对象通常缩写为 BO。

DRM_GEM_FOPS

DRM_GEM_FOPS

默认的 drm GEM 文件操作

描述

此宏提供了一种简写方式,用于在 file_operations 结构中设置 GEM 文件操作。如果您只需要默认操作,请改用 DEFINE_DRM_GEM_FOPS。

DEFINE_DRM_GEM_FOPS

DEFINE_DRM_GEM_FOPS (name)

为 GEM 驱动程序生成文件操作的宏

参数

name

生成的结构的名称

描述

此宏为基于 GEM 的驱动程序自动生成一个合适的 struct file_operations,该结构可以分配给 drm_driver.fops。请注意,此结构不能在驱动程序之间共享,因为它包含使用 THIS_MODULE 对当前模块的引用。

请注意,该声明已标记为静态 - 如果你需要此声明的非静态版本,你可能做错了,并且会意外破坏 THIS_MODULE 引用。

void drm_gem_object_get(struct drm_gem_object *obj)

获取 GEM 缓冲区对象引用

参数

struct drm_gem_object *obj

GEM 缓冲区对象

描述

此函数获取对 obj 的额外引用。在没有持有引用的情况下调用此函数是非法的。无需锁。

void drm_gem_object_put(struct drm_gem_object *obj)

丢弃 GEM 缓冲区对象引用

参数

struct drm_gem_object *obj

GEM 缓冲区对象

描述

此函数释放对 obj 的引用。

bool drm_gem_object_is_shared_for_memory_stats(struct drm_gem_object *obj)

用于共享内存统计的辅助函数

参数

struct drm_gem_object *obj

有问题的 obj

描述

此辅助函数仅应用于 fdinfo 共享内存统计,以确定 GEM 对象是否被共享。

drm_gem_gpuva_set_lock

drm_gem_gpuva_set_lock (obj, lock)

设置保护对 gpuva 列表访问的锁。

参数

obj

the drm_gem_object

用于保护 gpuva 列表的锁。锁定原语必须包含 dep_map 字段。

描述

如果您不使用 dma-resv 锁,而是使用自定义锁来保护对 gpuva 列表的访问,请调用此函数。

void drm_gem_gpuva_init(struct drm_gem_object *obj)

初始化 GEM 对象的 gpuva 列表

参数

struct drm_gem_object *obj

the drm_gem_object

描述

此函数初始化 drm_gem_objectdrm_gpuvm_bo 列表。

只有打算支持 drm_driver_feature DRIVER_GEM_GPUVA 的驱动程序才需要调用此函数。

另请参阅 drm_gem_gpuva_set_lock()

drm_gem_for_each_gpuvm_bo

drm_gem_for_each_gpuvm_bo (entry__, obj__)

遍历 drm_gpuvm_bo 列表的迭代器

参数

entry__

在每个迭代步骤中分配的 drm_gpuvm_bo 结构

obj__

与要遍历的 drm_gpuvm_bo 相关联的 drm_gem_object

描述

此迭代器遍历与 drm_gem_object 相关联的所有 drm_gpuvm_bo 结构。

drm_gem_for_each_gpuvm_bo_safe

drm_gem_for_each_gpuvm_bo_safe (entry__, next__, obj__)

安全地遍历 drm_gpuvm_bo 列表的迭代器

参数

entry__

在每个迭代步骤中分配的 drm_gpuvm_bostructure

next__

用于存储下一步的 next drm_gpuvm_bo

obj__

与要遍历的 drm_gpuvm_bo 相关联的 drm_gem_object

描述

此迭代器遍历与 drm_gem_object 相关联的所有 drm_gpuvm_bo 结构。它使用 list_for_each_entry_safe() 实现,因此可以安全地删除元素。

int drm_gem_object_init_with_mnt(struct drm_device *dev, struct drm_gem_object *obj, size_t size, struct vfsmount *gemfs)

在给定的 shmfs 挂载点初始化已分配的、由 shmem 支持的 GEM 对象

参数

struct drm_device *dev

应该为其初始化对象的 drm_device

struct drm_gem_object *obj

要初始化的 drm_gem_object

size_t size

对象大小

struct vfsmount *gemfs

将在其中创建 GEM 对象的 tmpfs 挂载点。如果为 NULL,则使用通常的 tmpfs 挂载点 (shm_mnt)。

描述

使用 shmfs 后备存储初始化指定大小的已分配 GEM 对象。

int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size)

初始化已分配的、由 shmem 支持的 GEM 对象

参数

struct drm_device *dev

应该为其初始化对象的 drm_device

struct drm_gem_object *obj

要初始化的 drm_gem_object

size_t size

对象大小

描述

使用 shmfs 后备存储初始化指定大小的已分配 GEM 对象。

void drm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size)

初始化已分配的私有 GEM 对象

参数

struct drm_device *dev

应该为其初始化对象的 drm_device

struct drm_gem_object *obj

要初始化的 drm_gem_object

size_t size

对象大小

描述

使用未提供的 GEM 后备存储初始化指定大小的已分配 GEM 对象。相反,调用者负责后备对象并处理它。

void drm_gem_private_object_fini(struct drm_gem_object *obj)

完成失败的 drm_gem_object

参数

struct drm_gem_object *obj

drm_gem_object

描述

当初始化失败时,取消初始化已分配的 GEM 对象

int drm_gem_handle_delete(struct drm_file *filp, u32 handle)

删除给定的文件私有句柄

参数

struct drm_file *filp

用于句柄查找的 drm 文件私有结构

u32 handle

要删除的用户空间句柄

描述

filp 查找表中移除通过 drm_gem_handle_create() 添加的 GEM 句柄。如果这是最后一个句柄,还会清理链接的资源,如 GEM 名称。

int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, u32 handle, u64 *offset)

返回 gem 对象的伪 mmap 偏移量

参数

struct drm_file *file

包含 gem 对象的 drm 文件私有结构

struct drm_device *dev

对应的 drm_device

u32 handle

gem 对象句柄

u64 *offset

伪 mmap 偏移量的返回位置

描述

这实现了 drm_driver.dumb_map_offset KMS 驱动回调,用于使用 gem 管理其后备存储的驱动程序。

返回

成功时返回 0,失败时返回负错误代码。

int drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, u32 *handlep)

为对象创建一个 gem 句柄

参数

struct drm_file *file_priv

用于注册句柄的 drm 文件私有结构

struct drm_gem_object *obj

要注册的对象

u32 *handlep

指向将创建的句柄返回给调用者的指针

描述

为此对象创建一个句柄。 这会向对象添加一个句柄引用,其中包括一个常规的引用计数。 调用者很可能希望在之后取消对该对象的引用。

由于这会将 obj 发布到用户空间,因此到此时它必须完全设置好,驱动程序必须在其缓冲区对象创建回调中最后调用此函数。

void drm_gem_free_mmap_offset(struct drm_gem_object *obj)

释放对象的伪 mmap 偏移量

参数

struct drm_gem_object *obj

有问题的 obj

描述

此例程释放 drm_gem_create_mmap_offset() 分配的伪偏移量。

请注意,drm_gem_object_release() 已经调用了此函数,因此驱动程序在释放 GEM 对象时不必自己处理释放 mmap 偏移量的问题。

int drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size)

为对象创建一个伪 mmap 偏移量

参数

struct drm_gem_object *obj

有问题的 obj

size_t size

虚拟大小

描述

GEM 内存映射的工作原理是将一个伪 mmap 偏移量返回给用户空间,以便用户空间可以在后续的 mmap(2) 调用中使用。 然后,DRM 核心代码根据偏移量查找对象并设置各种内存映射结构。

当虚拟大小与物理大小不同时(即 drm_gem_object.size),此例程会为 obj 分配并附加一个伪偏移量。 否则,只需使用 drm_gem_create_mmap_offset()

此函数是幂等的,并且可以透明地处理已分配的 mmap 偏移量。 驱动程序无需检查这种情况。

int drm_gem_create_mmap_offset(struct drm_gem_object *obj)

为对象创建一个伪 mmap 偏移量

参数

struct drm_gem_object *obj

有问题的 obj

描述

GEM 内存映射的工作原理是将一个伪 mmap 偏移量返回给用户空间,以便用户空间可以在后续的 mmap(2) 调用中使用。 然后,DRM 核心代码根据偏移量查找对象并设置各种内存映射结构。

此例程为 obj 分配并附加一个伪偏移量。

驱动程序可以在释放 obj 之前调用 drm_gem_free_mmap_offset() 来再次释放伪偏移量。

struct page **drm_gem_get_pages(struct drm_gem_object *obj)

帮助程序从 shmem 为 GEM 对象分配后备页

参数

struct drm_gem_object *obj

有问题的 obj

描述

这将读取给定 gem 对象的 shmem 后备存储的页面数组。 返回一个页面数组。 如果未分配或换出页面,则会分配/换入所需的页面。 请注意,整个对象都由页面数组覆盖并固定在内存中。

使用 drm_gem_put_pages() 释放数组并取消固定所有页面。

这会使用 shmem 映射上设置的 GFP 掩码(请参阅 mapping_set_gfp_mask())。 如果您需要其他 GFP 掩码,则必须自己执行这些分配。

请注意,不允许在运行时更改 gfp 区域。 也就是说,必须使用与初始化期间设置的 gfp_zone(gfp) 相同的 gfp_zone(gfp) 来调用 shmem_read_mapping_page_gfp()。 如果您有特殊的区域约束,请在 drm_gem_object_init() 后通过 mapping_set_gfp_mask() 设置它们。 shmem-core 会注意在换入期间将页面保留在所需的区域中。

此函数仅对使用 drm_gem_object_init() 初始化的对象有效,但对于仅使用 drm_gem_private_object_init() 初始化的对象无效。

void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, bool dirty, bool accessed)

帮助程序释放 GEM 对象的后备页

参数

struct drm_gem_object *obj

有问题的 obj

struct page **pages

要释放的页

bool dirty

如果为 true,则页面将被标记为脏

bool accessed

如果为 true,则页面将被标记为已访问

int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles, int count, struct drm_gem_object ***objs_out)

从句柄数组中查找 GEM 对象

参数

struct drm_file *filp

DRM 文件私有数据

void __user *bo_handles

指向用户空间句柄数组的用户指针

int count

句柄数组的大小

struct drm_gem_object ***objs_out

返回指向 drm_gem_object 指针数组的指针

描述

接受一个用户空间句柄数组,并返回一个新分配的 GEM 对象数组。

对于单个句柄查找,请使用 drm_gem_object_lookup()

返回

objs 使用 GEM 对象指针填充。返回的 GEM 对象需要使用 drm_gem_object_put() 释放。如果查找失败,则返回 -ENOENT。成功返回 0。

struct drm_gem_object *drm_gem_object_lookup(struct drm_file *filp, u32 handle)

从句柄查找 GEM 对象

参数

struct drm_file *filp

DRM 文件私有数据

u32 handle

用户空间句柄

描述

如果查找句柄数组,请使用 drm_gem_objects_lookup()

返回

如果 filp 上存在由句柄命名的对象,则返回该对象的引用;否则返回 NULL。

long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle, bool wait_all, unsigned long timeout)

等待 GEM 对象的预留对象的共享和/或独占围栏。

参数

struct drm_file *filep

DRM 文件私有数据

u32 handle

用户空间句柄

bool wait_all

如果为 true,则等待所有围栏,否则仅等待独占围栏

unsigned long timeout

以节拍为单位的超时值,或者为零以立即返回

返回

如果被中断,则返回 -ERESTARTSYS;如果等待超时,则返回 0;如果成功,则返回大于 0 的值。

void drm_gem_object_release(struct drm_gem_object *obj)

释放 GEM 缓冲区对象资源

参数

struct drm_gem_object *obj

GEM 缓冲区对象

描述

这将释放 obj 使用的任何结构和资源,并且是 drm_gem_object_init() 的逆操作。

void drm_gem_object_free(struct kref *kref)

释放 GEM 对象

参数

struct kref *kref

要释放的对象的 kref

描述

在丢失对该对象的最后一个引用后调用。

释放该对象

void drm_gem_vm_open(struct vm_area_struct *vma)

GEM 的 vma->ops->open 实现

参数

struct vm_area_struct *vma

VM 区域结构

描述

此函数为 GEM 驱动程序实现 #vm_operations_struct open() 回调。必须与 drm_gem_vm_close() 一起使用。

void drm_gem_vm_close(struct vm_area_struct *vma)

GEM 的 vma->ops->close 实现

参数

struct vm_area_struct *vma

VM 区域结构

描述

此函数为 GEM 驱动程序实现 #vm_operations_struct close() 回调。必须与 drm_gem_vm_open() 一起使用。

int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, struct vm_area_struct *vma)

内存映射 GEM 对象

参数

struct drm_gem_object *obj

要映射的 GEM 对象

unsigned long obj_size

要映射的对象大小,以字节为单位

struct vm_area_struct *vma

要映射区域的 VMA

描述

设置 VMA 以准备使用 GEM 对象的 vm_ops 映射 GEM 对象。根据他们的要求,GEM 对象可以在他们的 vm_ops 中提供一个故障处理程序(在这种情况下,对该对象的任何访问都将被捕获,以执行迁移、GTT 绑定、表面寄存器分配或性能监视),或者在调用 drm_gem_mmap_obj 后同步 mmap 缓冲区内存。

此函数主要用于实现 DMABUF mmap 操作,当 GEM 对象不是基于其伪偏移量查找时。要实现 DRM mmap 操作,驱动程序应使用 drm_gem_mmap() 函数。

drm_gem_mmap_obj() 假设用户被授予访问缓冲区的权限,而 drm_gem_mmap() 阻止非特权用户映射随机对象。因此,调用者必须在调用此辅助函数之前验证访问限制。

如果对象大小小于 VMA 大小,或者未提供 vm_ops,则返回 0 或成功,或 -EINVAL。

int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)

GEM 对象的内存映射例程

参数

struct file *filp

DRM 文件指针

struct vm_area_struct *vma

要映射区域的 VMA

描述

如果驱动程序支持 GEM 对象映射,则对 DRM 文件描述符的 mmap 调用将在此处结束。

根据传入的偏移量查找 GEM 对象(vma->vm_pgoff 将包含我们在对对象调用 GTT 映射 ioctl 时创建的伪偏移量),并使用对 drm_gem_mmap_obj() 的调用对其进行映射。

如果未授予调用者访问缓冲区对象的权限,则 mmap 将失败,并返回 EACCES。请参阅 VMA 管理器以获取更多信息。

int drm_gem_lock_reservations(struct drm_gem_object **objs, int count, struct ww_acquire_ctx *acquire_ctx)

设置 ww 上下文并获取 GEM 对象数组上的锁。

参数

struct drm_gem_object **objs

要锁定的 drm_gem_object

int count

objs 中的对象数量

struct ww_acquire_ctx *acquire_ctx

将作为跟踪此锁定预留集合的一部分进行初始化的 struct ww_acquire_ctx。

描述

锁定预留后,您需要为共享栅栏(如果适用)设置空间,提交作业,然后调用 drm_gem_unlock_reservations()。

void drm_gem_lru_init(struct drm_gem_lru *lru, struct mutex *lock)

初始化 LRU

参数

struct drm_gem_lru *lru

要初始化的 LRU

struct mutex *lock

保护 LRU 的锁

void drm_gem_lru_remove(struct drm_gem_object *obj)

从它所在的任何 LRU 中移除对象

参数

struct drm_gem_object *obj

要从当前 LRU 中移除的 GEM 对象

描述

如果对象当前在任何 LRU 中,则将其移除。

void drm_gem_lru_move_tail_locked(struct drm_gem_lru *lru, struct drm_gem_object *obj)

将对象移动到 LRU 的尾部

参数

struct drm_gem_lru *lru

要将对象移动到的 LRU。

struct drm_gem_object *obj

要移动到此 LRU 的 GEM 对象

描述

类似于 drm_gem_lru_move_tail,但必须持有 lru 锁

void drm_gem_lru_move_tail(struct drm_gem_lru *lru, struct drm_gem_object *obj)

将对象移动到 LRU 的尾部

参数

struct drm_gem_lru *lru

要将对象移动到的 LRU。

struct drm_gem_object *obj

要移动到此 LRU 的 GEM 对象

描述

如果对象已在此 LRU 中,则将其移动到尾部。否则,将其从它所在的任何其他 LRU 中移除(如果有),并移动到此 LRU 中。

unsigned long drm_gem_lru_scan(struct drm_gem_lru *lru, unsigned int nr_to_scan, unsigned long *remaining, bool (*shrink)(struct drm_gem_object *obj))

用于实现收缩器 scan_objects 的助手函数

参数

struct drm_gem_lru *lru

要扫描的 LRU

unsigned int nr_to_scan

要尝试回收的页数

unsigned long *remaining

要回收的剩余页数,应由调用者初始化

bool (*shrink)(struct drm_gem_object *obj)

尝试收缩/回收对象的回调函数。

描述

如果收缩回调成功,则期望驱动程序将对象移出此 LRU。

如果 LRU 可能包含活动缓冲区,则收缩回调有责任检查这一点(例如,dma_resv_test_signaled())或在必要时阻塞,直到缓冲区变为空闲。

int drm_gem_evict(struct drm_gem_object *obj)

用于驱逐 GEM 对象后备页的助手函数

参数

struct drm_gem_object *obj

有问题的 obj

GEM DMA 助手函数参考

DRM GEM/DMA 助手是一种提供以连续内存块形式呈现给设备的缓冲区对象的方法。 这对于不支持散点/收集 DMA(直接或通过使用紧密连接的 IOMMU)的设备很有用。

对于通过(外部)IOMMU 访问内存总线的设备,则使用传统的基于页面的分配器来分配缓冲区对象,并且可以分散在物理内存中。但是,它们在 IOVA 空间中是连续的,因此对于使用它们的设备而言是连续的。

对于其他设备,助手依赖 CMA 来提供物理上连续的内存中的缓冲区对象。

对于 struct drm_gem_object 函数中的 GEM 回调助手,请参阅具有 _object_ 中缀的类似名称的函数(例如,drm_gem_dma_object_vmap() 包装 drm_gem_dma_vmap())。这些助手执行必要的类型转换。

struct drm_gem_dma_object

由 DMA 内存分配支持的 GEM 对象

定义:

struct drm_gem_dma_object {
    struct drm_gem_object base;
    dma_addr_t dma_addr;
    struct sg_table *sgt;
    void *vaddr;
    bool map_noncoherent;
};

成员

base

基本 GEM 对象

dma_addr

后备内存的 DMA 地址

sgt

用于导入的 PRIME 缓冲区的散点/收集表。 该表可以有多个条目,但保证它们具有连续的 DMA 地址。

vaddr

后备内存的内核虚拟地址

map_noncoherent

如果为 true,则 GEM 对象由非一致性内存支持

void drm_gem_dma_object_free(struct drm_gem_object *obj)

drm_gem_dma_free() 的 GEM 对象函数

参数

struct drm_gem_object *obj

要释放的 GEM 对象

描述

此函数包装 drm_gem_dma_free_object()。 采用 DMA 助手的驱动程序应将其用作其 drm_gem_object_funcs.free 处理程序。

void drm_gem_dma_object_print_info(struct drm_printer *p, unsigned int indent, const struct drm_gem_object *obj)

为 debugfs 打印 drm_gem_dma_object 信息

参数

struct drm_printer *p

DRM 打印机

unsigned int indent

制表符缩进级别

const struct drm_gem_object *obj

GEM 对象

描述

此函数封装了 drm_gem_dma_print_info()。使用 DMA 助手的驱动程序应使用此函数作为其 drm_gem_object_funcs.print_info 处理程序。

struct sg_table *drm_gem_dma_object_get_sg_table(struct drm_gem_object *obj)

用于 drm_gem_dma_get_sg_table() 的 GEM 对象函数

参数

struct drm_gem_object *obj

GEM 对象

描述

此函数封装了 drm_gem_dma_get_sg_table()。使用 DMA 助手的驱动程序应使用它作为其 drm_gem_object_funcs.get_sg_table 处理程序。

返回

指向已固定页面的散布/收集表的指针,如果失败则为 NULL。

int drm_gem_dma_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)

用于 drm_gem_dma_mmap() 的 GEM 对象函数

参数

struct drm_gem_object *obj

GEM 对象

struct vm_area_struct *vma

要映射区域的 VMA

描述

此函数封装了 drm_gem_dma_mmap()。使用 DMA 助手的驱动程序应使用它作为其 drm_gem_object_funcs.mmap 处理程序。

返回

成功时返回 0,失败时返回负错误代码。

DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE

DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE (dumb_create_func)

DMA GEM 驱动程序操作

参数

dumb_create_func

用于 .dumb_create 的回调函数

描述

此宏为在 drm_driver 结构中设置默认 GEM 操作提供快捷方式。

对于覆盖 struct rm_driver.dumb_create 默认实现的驱动程序,此宏是 DRM_GEM_DMA_DRIVER_OPS 的变体。如果可能,请使用 DRM_GEM_DMA_DRIVER_OPS。需要在导入的缓冲区上使用虚拟地址的驱动程序应使用 DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE() 代替。

DRM_GEM_DMA_DRIVER_OPS

DRM_GEM_DMA_DRIVER_OPS

DMA GEM 驱动程序操作

描述

此宏为在 drm_driver 结构中设置默认 GEM 操作提供快捷方式。

对于带有自己的 struct drm_driver.dumb_create 实现的驱动程序,应使用 DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE() 代替。如果可能,请使用 DRM_GEM_DMA_DRIVER_OPS。需要在导入的缓冲区上使用虚拟地址的驱动程序应使用 DRM_GEM_DMA_DRIVER_OPS_VMAP 代替。

DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE

DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE (dumb_create_func)

DMA GEM 驱动程序操作,确保缓冲区上的虚拟地址

参数

dumb_create_func

用于 .dumb_create 的回调函数

描述

此宏为在 drm_driver 结构中设置默认 GEM 操作提供快捷方式,适用于也需要在导入的缓冲区上使用虚拟地址的驱动程序。

对于覆盖 struct drm_driver.dumb_create 默认实现的驱动程序,此宏是 DRM_GEM_DMA_DRIVER_OPS_VMAP 的变体。如果可能,请使用 DRM_GEM_DMA_DRIVER_OPS_VMAP。不需要在导入的缓冲区上使用虚拟地址的驱动程序应使用 DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE() 代替。

DRM_GEM_DMA_DRIVER_OPS_VMAP

DRM_GEM_DMA_DRIVER_OPS_VMAP

DMA GEM 驱动程序操作,确保缓冲区上的虚拟地址

描述

此宏为在 drm_driver 结构中设置默认 GEM 操作提供快捷方式,适用于也需要在导入的缓冲区上使用虚拟地址的驱动程序。

对于带有自己的 struct drm_driver.dumb_create 实现的驱动程序,应使用 DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE() 代替。如果可能,请使用 DRM_GEM_DMA_DRIVER_OPS_VMAP。不需要在导入的缓冲区上使用虚拟地址的驱动程序应使用 DRM_GEM_DMA_DRIVER_OPS 代替。

DEFINE_DRM_GEM_DMA_FOPS

DEFINE_DRM_GEM_DMA_FOPS (name)

用于为 DMA 驱动程序生成文件操作的宏

参数

name

生成的结构的名称

描述

此宏自动为基于 DMA 的驱动程序生成一个合适的 struct file_operations,可以将其分配给 drm_driver.fops。请注意,此结构不能在驱动程序之间共享,因为它包含对使用 THIS_MODULE 的当前模块的引用。

请注意,该声明已标记为静态 - 如果你需要此声明的非静态版本,你可能做错了,并且会意外破坏 THIS_MODULE 引用。

struct drm_gem_dma_object *drm_gem_dma_create(struct drm_device *drm, size_t size)

分配具有给定大小的对象

参数

struct drm_device *drm

DRM 设备

size_t size

要分配的对象的大小

描述

此函数创建一个 DMA GEM 对象,并分配内存作为后备存储。分配的内存将占用总线地址空间的连续块。

对于直接连接到内存总线的设备,分配的内存将在物理上是连续的。对于通过 IOMMU 访问的设备,分配的内存不期望在物理上是连续的,因为拥有连续的 IOVA 足以满足设备的 DMA 要求。

返回

成功时为 struct drm_gem_dma_object *,失败时为 ERR_PTR() 编码的负错误代码。

void drm_gem_dma_free(struct drm_gem_dma_object *dma_obj)

释放与 DMA GEM 对象关联的资源

参数

struct drm_gem_dma_object *dma_obj

要释放的 DMA GEM 对象

描述

此函数释放 DMA GEM 对象的后备内存,清理 GEM 对象状态,并释放用于存储对象本身的内存。如果缓冲区是导入的且设置了虚拟地址,则会释放该虚拟地址。

int drm_gem_dma_dumb_create_internal(struct drm_file *file_priv, struct drm_device *drm, struct drm_mode_create_dumb *args)

创建一个哑缓冲区对象

参数

struct drm_file *file_priv

用于创建哑缓冲区的 DRM 文件私有结构

struct drm_device *drm

DRM 设备

struct drm_mode_create_dumb *args

IOCTL 数据

描述

此函数将 pitch 和 size 参数对齐到最小要求。这是一个内部辅助函数,驱动程序可以将其包装起来,以处理具有更特定对齐要求的硬件。它不应直接用作它们的 drm_driver.dumb_create 回调。

返回

成功时返回 0,失败时返回负错误代码。

int drm_gem_dma_dumb_create(struct drm_file *file_priv, struct drm_device *drm, struct drm_mode_create_dumb *args)

创建一个哑缓冲区对象

参数

struct drm_file *file_priv

用于创建哑缓冲区的 DRM 文件私有结构

struct drm_device *drm

DRM 设备

struct drm_mode_create_dumb *args

IOCTL 数据

描述

此函数计算哑缓冲区的 pitch,并将其向上舍入为每像素的整数个字节。对于对 pitch 没有其他限制的硬件,驱动程序可以直接使用此函数作为其 drm_driver.dumb_create 回调。

对于具有其他限制的硬件,驱动程序可以调整用户空间设置的字段,并将 IOCTL 数据传递给 drm_gem_dma_dumb_create_internal() 函数。

返回

成功时返回 0,失败时返回负错误代码。

unsigned long drm_gem_dma_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)

在 noMMU 情况下,为映射提出地址

参数

struct file *filp

文件对象

unsigned long addr

内存地址

unsigned long len

缓冲区大小

unsigned long pgoff

页面偏移

unsigned long flags

内存标志

描述

此函数用于 noMMU 平台,以提出给定缓冲区的地址映射。它旨在用作 struct file_operations.get_unmapped_area 操作的直接处理程序。

返回

成功时的映射地址,或失败时的负错误代码。

void drm_gem_dma_print_info(const struct drm_gem_dma_object *dma_obj, struct drm_printer *p, unsigned int indent)

为 debugfs 打印 drm_gem_dma_object 信息

参数

const struct drm_gem_dma_object *dma_obj

DMA GEM 对象

struct drm_printer *p

DRM 打印机

unsigned int indent

制表符缩进级别

描述

此函数打印 dma_addr 和 vaddr,用于例如 debugfs 输出。

struct sg_table *drm_gem_dma_get_sg_table(struct drm_gem_dma_object *dma_obj)

为 DMA GEM 对象提供固定页面的分散/聚集表

参数

struct drm_gem_dma_object *dma_obj

DMA GEM 对象

描述

此函数通过调用标准 DMA 映射 API 导出分散/聚集表。

返回

指向已固定页面的散布/收集表的指针,如果失败则为 NULL。

struct drm_gem_object *drm_gem_dma_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt)

从另一个驱动程序的固定页面的分散/聚集表生成 DMA GEM 对象

参数

struct drm_device *dev

要导入到的设备

struct dma_buf_attachment *attach

DMA-BUF 附件

struct sg_table *sgt

固定页面的分散/聚集表

描述

此函数导入由另一个驱动程序通过 DMA-BUF 导出的分散/聚集表。导入的缓冲区必须在内存中物理连续(即,分散/聚集表必须包含单个条目)。使用 DMA 帮助程序的驱动程序应将其设置为其 drm_driver.gem_prime_import_sg_table 回调。

返回

指向新创建的 GEM 对象的指针,或失败时指向 ERR_PTR 编码的负错误代码。

int drm_gem_dma_vmap(struct drm_gem_dma_object *dma_obj, struct iosys_map *map)

将 DMA GEM 对象映射到内核的虚拟地址空间

参数

struct drm_gem_dma_object *dma_obj

DMA GEM 对象

struct iosys_map *map

返回 DMA GEM 对象后备存储的内核虚拟地址。

描述

此函数将缓冲区映射到内核的虚拟地址空间。由于 DMA 缓冲区已映射到内核虚拟地址空间,因此它仅返回缓存的虚拟地址。

返回

成功时返回 0,否则返回负错误代码。

int drm_gem_dma_mmap(struct drm_gem_dma_object *dma_obj, struct vm_area_struct *vma)

内存映射导出的 DMA GEM 对象

参数

struct drm_gem_dma_object *dma_obj

DMA GEM 对象

struct vm_area_struct *vma

要映射区域的 VMA

描述

此函数将缓冲区映射到用户空间进程的地址空间。除了通常的 GEM VMA 设置之外,它还会立即将整个对象调入,而不是使用按需调入。

返回

成功时返回 0,失败时返回负错误代码。

struct drm_gem_object *drm_gem_dma_prime_import_sg_table_vmap(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt)

PRIME 导入另一个驱动程序的散列表/收集表,并获取缓冲区的虚拟地址

参数

struct drm_device *dev

DRM 设备

struct dma_buf_attachment *attach

DMA-BUF 附件

struct sg_table *sgt

已固定页面的散列表/收集表

描述

此函数使用 drm_gem_dma_prime_import_sg_table() 导入散列表/收集表,并使用 dma_buf_vmap() 获取内核虚拟地址。这确保了 DMA GEM 对象始终设置了其虚拟地址。此地址在对象释放时被释放。

此函数可用作 drm_driver.gem_prime_import_sg_table 回调函数。DRM_GEM_DMA_DRIVER_OPS_VMAP 宏提供了一个快捷方式来设置必要的 DRM 驱动程序操作。

返回

指向新创建的 GEM 对象的指针,或失败时指向 ERR_PTR 编码的负错误代码。

GEM SHMEM 辅助函数参考

此库为由使用匿名可分页内存分配的 shmem 缓冲区支持的 GEM 对象提供辅助函数。

在 GEM 对象上运行的函数接收 struct drm_gem_shmem_object。对于 struct drm_gem_object 函数中的 GEM 回调辅助函数,请参阅名称相似的带有 _object_ 中缀的函数(例如,drm_gem_shmem_object_vmap() 包装了 drm_gem_shmem_vmap())。这些辅助函数执行必要的类型转换。

struct drm_gem_shmem_object

由 shmem 支持的 GEM 对象

定义:

struct drm_gem_shmem_object {
    struct drm_gem_object base;
    struct page **pages;
    unsigned int pages_use_count;
    int madv;
    struct list_head madv_list;
    struct sg_table *sgt;
    void *vaddr;
    unsigned int vmap_use_count;
    bool pages_mark_dirty_on_put : 1;
    bool pages_mark_accessed_on_put : 1;
    bool map_wc : 1;
};

成员

base

基础 GEM 对象

pages

页表

pages_use_count

页表上的引用计数。当计数达到零时,页面会被放入。

madv

madvise 的状态

0 表示激活/正在使用。负值表示对象已被清除。正值是特定于驱动程序的,辅助函数不使用。

madv_list

用于 madvise 跟踪的列表条目

通常由驱动程序用来跟踪可清除的对象

sgt

用于导入的 PRIME 缓冲区的散列表/收集表

vaddr

后备内存的内核虚拟地址

vmap_use_count

虚拟地址上的引用计数。当计数达到零时,地址将被取消映射。

pages_mark_dirty_on_put

当页面被放入时,将其标记为脏。

pages_mark_accessed_on_put

当页面被放入时,将其标记为已访问。

map_wc

映射对象写入合并(而不是使用 shmem 默认值)。

void drm_gem_shmem_object_free(struct drm_gem_object *obj)

用于 drm_gem_shmem_free() 的 GEM 对象函数

参数

struct drm_gem_object *obj

要释放的 GEM 对象

描述

此函数包装了 drm_gem_shmem_free()。使用 shmem 辅助函数的驱动程序应将其用作 drm_gem_object_funcs.free 处理程序。

void drm_gem_shmem_object_print_info(struct drm_printer *p, unsigned int indent, const struct drm_gem_object *obj)

打印用于 debugfs 的 drm_gem_shmem_object 信息

参数

struct drm_printer *p

DRM 打印机

unsigned int indent

制表符缩进级别

const struct drm_gem_object *obj

GEM 对象

描述

此函数包装了 drm_gem_shmem_print_info()。使用 shmem 辅助函数的驱动程序应将此函数用作 drm_gem_object_funcs.print_info 处理程序。

int drm_gem_shmem_object_pin(struct drm_gem_object *obj)

用于 drm_gem_shmem_pin() 的 GEM 对象函数

参数

struct drm_gem_object *obj

GEM 对象

描述

此函数包装了 drm_gem_shmem_pin()。使用 shmem 辅助函数的驱动程序应将其用作 drm_gem_object_funcs.pin 处理程序。

void drm_gem_shmem_object_unpin(struct drm_gem_object *obj)

用于 drm_gem_shmem_unpin() 的 GEM 对象函数

参数

struct drm_gem_object *obj

GEM 对象

描述

此函数包装了 drm_gem_shmem_unpin()。使用 shmem 辅助函数的驱动程序应将其用作 drm_gem_object_funcs.unpin 处理程序。

struct sg_table *drm_gem_shmem_object_get_sg_table(struct drm_gem_object *obj)

用于 drm_gem_shmem_get_sg_table() 的 GEM 对象函数

参数

struct drm_gem_object *obj

GEM 对象

描述

此函数包装了 drm_gem_shmem_get_sg_table()。使用 shmem 辅助函数的驱动程序应将其用作 drm_gem_object_funcs.get_sg_table 处理程序。

返回

指向已固定页面的散列表/收集表的指针,如果失败,则为错误指针。

int drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)

用于 drm_gem_shmem_mmap() 的 GEM 对象函数

参数

struct drm_gem_object *obj

GEM 对象

struct vm_area_struct *vma

要映射区域的 VMA

描述

此函数包装了 drm_gem_shmem_mmap()。使用 shmem 辅助函数的驱动程序应将其用作 drm_gem_object_funcs.mmap 处理程序。

返回

成功时返回 0,失败时返回负错误代码。

DRM_GEM_SHMEM_DRIVER_OPS

DRM_GEM_SHMEM_DRIVER_OPS

默认的 shmem GEM 操作

描述

此宏为在 drm_driver 结构中设置 shmem GEM 操作提供了一个快捷方式。

struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size)

分配一个具有给定大小的对象

参数

struct drm_device *dev

DRM 设备

size_t size

要分配的对象的大小

描述

此函数创建一个 shmem GEM 对象。

返回

成功时返回 struct drm_gem_shmem_object *,失败时返回 ERR_PTR() 编码的负错误代码。

struct drm_gem_shmem_object *drm_gem_shmem_create_with_mnt(struct drm_device *dev, size_t size, struct vfsmount *gemfs)

在给定的挂载点中分配一个具有给定大小的对象

参数

struct drm_device *dev

DRM 设备

size_t size

要分配的对象的大小

struct vfsmount *gemfs

将在其中创建 GEM 对象的 tmpfs 挂载点

描述

此函数在给定的 tmpfs 挂载点中创建一个 shmem GEM 对象。

返回

成功时返回 struct drm_gem_shmem_object *,失败时返回 ERR_PTR() 编码的负错误代码。

void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)

释放与 shmem GEM 对象关联的资源

参数

struct drm_gem_shmem_object *shmem

要释放的 shmem GEM 对象

描述

此函数清理 GEM 对象状态并释放用于存储对象本身的内存。

int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem)

为 shmem GEM 对象锁定后备页

参数

struct drm_gem_shmem_object *shmem

shmem GEM 对象

描述

此函数确保在导出缓冲区时将后备页锁定在内存中。

返回

成功时返回 0,失败时返回负错误代码。

void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem)

为 shmem GEM 对象解除锁定后备页

参数

struct drm_gem_shmem_object *shmem

shmem GEM 对象

描述

此函数取消后备页必须锁定在内存中的要求。

int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args)

创建一个哑 shmem 缓冲区对象

参数

struct drm_file *file

创建哑缓冲区的 DRM 文件结构

struct drm_device *dev

DRM 设备

struct drm_mode_create_dumb *args

IOCTL 数据

描述

此函数计算哑缓冲区的 pitch,并将其向上舍入为每像素的整数个字节。对于对 pitch 没有其他限制的硬件,驱动程序可以直接使用此函数作为其 drm_driver.dumb_create 回调。

对于具有其他限制的硬件,驱动程序可以在调用此函数之前调整用户空间设置的字段。

返回

成功时返回 0,失败时返回负错误代码。

int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma)

内存映射一个 shmem GEM 对象

参数

struct drm_gem_shmem_object *shmem

shmem GEM 对象

struct vm_area_struct *vma

要映射区域的 VMA

描述

此函数为 shmem 对象实现 GEM DRM 文件 mmap 操作的增强版本。

返回

成功时返回 0,失败时返回负错误代码。

void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem, struct drm_printer *p, unsigned int indent)

打印用于 debugfs 的 drm_gem_shmem_object 信息

参数

const struct drm_gem_shmem_object *shmem

shmem GEM 对象

struct drm_printer *p

DRM 打印机

unsigned int indent

制表符缩进级别

struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem)

为 shmem GEM 对象提供锁定页面的散点/收集表

参数

struct drm_gem_shmem_object *shmem

shmem GEM 对象

描述

此函数通过调用标准 DMA 映射 API 导出适用于 PRIME 使用的散点/收集表。

需要为对象获取散点/收集表的驱动程序需要调用 drm_gem_shmem_get_pages_sgt()

返回

指向已固定页面的散列表/收集表的指针,如果失败,则为错误指针。

struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem)

锁定页面,进行 DMA 映射,并返回 shmem GEM 对象的散点/收集表。

参数

struct drm_gem_shmem_object *shmem

shmem GEM 对象

描述

此函数返回适用于驱动程序使用的散点/收集表。如果 sg 表不存在,则锁定页面,进行 dma 映射,并创建 sg 表。

这是驱动程序获取后备存储的主要函数,它隐藏了 dma-buf 导入对象和本机分配对象之间的差异。驱动程序不应直接调用 drm_gem_shmem_get_sg_table()

返回

指向锁定页面的散点/收集表的指针,或者失败时返回 errno。

struct drm_gem_object *drm_gem_shmem_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt)

从另一个驱动程序的锁定页面的散点/收集表生成 shmem GEM 对象

参数

struct drm_device *dev

要导入的设备

struct dma_buf_attachment *attach

DMA-BUF 附件

struct sg_table *sgt

已固定页面的散列表/收集表

描述

此函数导入由另一个驱动程序通过 DMA-BUF 导出的散点/收集表。使用 shmem 助手的驱动程序应将其设置为他们的 drm_driver.gem_prime_import_sg_table 回调。

返回

指向新创建的 GEM 对象的指针,或失败时指向 ERR_PTR 编码的负错误代码。

GEM VRAM 助手函数参考

此库提供 struct drm_gem_vram_object (GEM VRAM),这是一个由视频 RAM (VRAM) 支持的 GEM 缓冲区对象。它可用于具有专用内存的帧缓冲设备。

数据结构 struct drm_vram_mm 及其辅助函数为具有专用显存的简单帧缓冲设备实现内存管理器。GEM VRAM 缓冲区对象要么放置在显存中,要么保持被换出到系统内存中。

通过 GEM 接口,用户空间应用程序可以创建、管理和销毁图形缓冲区,例如屏幕上的帧缓冲。GEM 不提供这些接口的实现。这取决于 DRM 驱动程序提供适合硬件的实现。如果硬件设备包含专用显存,则 DRM 驱动程序可以使用 VRAM 辅助库。每个活动的缓冲区对象都存储在显存中。活动缓冲区用于绘制当前帧,通常类似于帧的扫描输出缓冲区或光标图像。如果 VRAM 中没有剩余空间,则可以将不活动的 GEM 对象移动到系统内存中。

要初始化 VRAM 辅助库,请调用 drmm_vram_helper_init()。该函数在 struct drm_device.vram_mm 中分配并初始化一个 struct drm_vram_mm 的实例。使用 DRM_GEM_VRAM_DRIVER 初始化 struct drm_driver,并使用 DRM_VRAM_MM_FILE_OPERATIONS 初始化 struct file_operations,如下所示。

struct file_operations fops ={
        .owner = THIS_MODULE,
        DRM_VRAM_MM_FILE_OPERATION
};
struct drm_driver drv = {
        .driver_feature = DRM_ ... ,
        .fops = &fops,
        DRM_GEM_VRAM_DRIVER
};

int init_drm_driver()
{
        struct drm_device *dev;
        uint64_t vram_base;
        unsigned long vram_size;
        int ret;

        // setup device, vram base and size
        // ...

        ret = drmm_vram_helper_init(dev, vram_base, vram_size);
        if (ret)
                return ret;
        return 0;
}

这将创建一个 struct drm_vram_mm 的实例,导出用于 GEM 缓冲区管理的 DRM 用户空间接口,并初始化文件操作以允许访问已创建的 GEM 缓冲区。通过这种设置,DRM 驱动程序使用 VRAM MM 管理显存区域,并向用户空间提供 GEM VRAM 对象。

您不必清理 VRAM MM 的实例。drmm_vram_helper_init() 是一个托管接口,它安装一个清理处理程序,在 DRM 设备释放期间运行。

对于绘制或扫描输出操作,相应的缓冲区对象必须固定在显存中。调用 drm_gem_vram_pin() 并使用 DRM_GEM_VRAM_PL_FLAG_VRAMDRM_GEM_VRAM_PL_FLAG_SYSTEM 将缓冲区对象固定在显存或系统内存中。之后调用 drm_gem_vram_unpin() 来释放固定的对象。

固定在显存中的缓冲区对象在该内存区域中具有固定地址。调用 drm_gem_vram_offset() 来检索该值。它通常用于为帧缓冲区编程硬件的扫描输出引擎、设置鼠标光标的游标叠加图像,或将其用作硬件绘图引擎的输入。

要从 DRM 驱动程序访问缓冲区对象的内存,请调用 drm_gem_vram_vmap()。它将缓冲区映射到内核地址空间并返回内存地址。使用 drm_gem_vram_vunmap() 来释放映射。

struct drm_gem_vram_object

由 VRAM 支持的 GEM 对象

定义:

struct drm_gem_vram_object {
    struct ttm_buffer_object bo;
    struct iosys_map map;
    unsigned int vmap_use_count;
    struct ttm_placement placement;
    struct ttm_place placements[2];
};

成员

bo

TTM 缓冲区对象

map

bo 的映射信息

vmap_use_count

虚拟地址上的引用计数。当计数达到零时,地址将被取消映射。

placement

TTM 位置信息。支持的位置是 TTM_PL_VRAMTTM_PL_SYSTEM

placements

TTM 位置信息。

描述

类型 struct drm_gem_vram_object 表示由 VRAM 支持的 GEM 对象。它可以用于具有专用内存的简单帧缓冲设备。如果显存变得稀缺,则可以将缓冲区对象换出到系统内存。

GEM VRAM 对象对 pin 和映射操作执行引用计数。因此,使用 drm_gem_vram_pin() 固定 N 次的缓冲区对象必须使用 drm_gem_vram_unpin() 解除固定 N 次。这同样适用于 drm_gem_vram_kmap() 和 drm_gem_vram_kunmap() 对,以及 drm_gem_vram_vmap()drm_gem_vram_vunmap() 对。

struct drm_gem_vram_object *drm_gem_vram_of_bo(struct ttm_buffer_object *bo)

返回字段 bo 的类型为 struct drm_gem_vram_object 的容器。

参数

struct ttm_buffer_object *bo

VRAM 缓冲区对象

返回

包含的 GEM VRAM 对象

struct drm_gem_vram_object *drm_gem_vram_of_gem(struct drm_gem_object *gem)

返回字段 gem 的类型为 struct drm_gem_vram_object 的容器。

参数

struct drm_gem_object *gem

GEM 对象

返回

包含的 GEM VRAM 对象

DRM_GEM_VRAM_PLANE_HELPER_FUNCS

DRM_GEM_VRAM_PLANE_HELPER_FUNCS

初始化用于 VRAM 处理的 struct drm_plane_helper_funcs

描述

驱动程序可以使用 GEM BO 作为帧缓冲区内存的 VRAM 助手。此宏初始化 struct drm_plane_helper_funcs 以使用相应的辅助函数。

DRM_GEM_VRAM_DRIVER

DRM_GEM_VRAM_DRIVER

struct drm_driver 的默认回调函数

描述

使用 VRAM MM 和 GEM VRAM 的驱动程序可以使用此宏初始化带有默认函数的 struct drm_driver

struct drm_vram_mm

VRAM MM 的实例

定义:

struct drm_vram_mm {
    uint64_t vram_base;
    size_t vram_size;
    struct ttm_device bdev;
};

成员

vram_base

托管的显存的基地址

vram_size

托管的显存的大小(以字节为单位)

bdev

TTM BO 设备。

描述

字段 struct drm_vram_mm.vram_base 和 struct drm_vram_mm.vrm_size 由 VRAM MM 管理,但可公开读取访问。使用字段 struct drm_vram_mm.bdev 来访问 TTM BO 设备。

struct drm_vram_mm *drm_vram_mm_of_bdev(struct ttm_device *bdev)

返回字段 bdev 的类型为 struct ttm_device 的容器。

参数

struct ttm_device *bdev

TTM BO 设备

返回

struct drm_vram_mm 的包含实例

struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, size_t size, unsigned long pg_align)

创建一个 VRAM 支持的 GEM 对象

参数

struct drm_device *dev

DRM 设备

size_t size

以字节为单位的缓冲区大小

unsigned long pg_align

缓冲区对齐的页大小的倍数

描述

GEM 对象通过调用 struct drm_driver.gem_create_object(如果设置)进行分配。否则,将使用 kzalloc()。驱动程序可以在 struct drm_driver.gem_create_object 中设置自己的 GEM 对象函数。如果没有设置函数,则新的 GEM 对象将使用来自 GEM VRAM 助手程序的默认函数。

返回

成功时返回 struct drm_gem_vram_object 的新实例,否则返回 ERR_PTR() 编码的错误代码。

void drm_gem_vram_put(struct drm_gem_vram_object *gbo)

释放对 VRAM 支持的 GEM 对象的引用

参数

struct drm_gem_vram_object *gbo

GEM VRAM 对象

描述

有关更多信息,请参阅 ttm_bo_put()。

s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo)

返回 GEM VRAM 对象在显存中的偏移量

参数

struct drm_gem_vram_object *gbo

GEM VRAM 对象

描述

此函数返回缓冲区对象在设备显存中的偏移量。缓冲区对象必须固定到 TTM_PL_VRAM

返回

成功时返回缓冲区对象在显存中的偏移量,否则返回负的 errno 代码。

int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag)

将 GEM VRAM 对象固定在某个区域中。

参数

struct drm_gem_vram_object *gbo

GEM VRAM 对象

unsigned long pl_flag

可能内存区域的位掩码

描述

固定缓冲区对象可确保它不会从内存区域中被逐出。固定的缓冲区对象必须先取消固定,才能固定到另一个区域。如果 pl_flag 参数为 0,则缓冲区会固定在其当前位置(显存或系统内存)。

如果小型缓冲区对象(例如光标图像)固定在显存的中间,可能会导致内存碎片。这在只有少量显存的设备上尤其是一个问题。碎片可能会阻止主帧缓冲区容纳,即使总体上有足够的内存。修饰符 DRM_GEM_VRAM_PL_FLAG_TOPDOWN 会将缓冲区对象标记为固定在内存区域的高端,以避免碎片。

返回

成功时返回 0,否则返回负错误代码。

int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo)

取消固定 GEM VRAM 对象

参数

struct drm_gem_vram_object *gbo

GEM VRAM 对象

返回

成功时返回 0,否则返回负错误代码。

int drm_gem_vram_vmap(struct drm_gem_vram_object *gbo, struct iosys_map *map)

将 GEM VRAM 对象固定并映射到内核地址空间

参数

struct drm_gem_vram_object *gbo

要映射的 GEM VRAM 对象

struct iosys_map *map

返回 VRAM GEM 对象后备存储的内核虚拟地址。

描述

vmap 函数将 GEM VRAM 对象固定到其当前位置(系统内存或显存),并将其缓冲区映射到内核地址空间。由于固定对象无法重定位,因此应避免永久固定对象。使用返回的地址调用 drm_gem_vram_vunmap() 以取消映射并取消固定 GEM VRAM 对象。

返回

成功时返回 0,否则返回负错误代码。

void drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo, struct iosys_map *map)

取消映射并取消固定 GEM VRAM 对象

参数

struct drm_gem_vram_object *gbo

要取消映射的 GEM VRAM 对象

struct iosys_map *map

VRAM GEM 对象映射到的内核虚拟地址

描述

调用 drm_gem_vram_vunmap() 会取消映射并取消固定 GEM VRAM 缓冲区。有关更多信息,请参阅 drm_gem_vram_vmap() 的文档。

int drm_gem_vram_fill_create_dumb(struct drm_file *file, struct drm_device *dev, unsigned long pg_align, unsigned long pitch_align, struct drm_mode_create_dumb *args)

用于实现 struct drm_driver.dumb_create 的助手

参数

struct drm_file *file

DRM 文件

struct drm_device *dev

DRM 设备

unsigned long pg_align

缓冲区对齐的页大小的倍数

unsigned long pitch_align

扫描线对齐的 2 的幂

struct drm_mode_create_dumb *args

提供给 struct drm_driver.dumb_create 的参数

描述

此帮助函数填充 struct drm_mode_create_dumb,该结构由 struct drm_driver.dumb_create 使用。此接口的实现应将其参数以及特定于驱动程序的参数转发到此助手。

返回

成功时返回 0,否则返回负错误代码。

int drm_gem_vram_driver_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args)

实现 struct drm_driver.dumb_create

参数

struct drm_file *file

DRM 文件

struct drm_device *dev

DRM 设备

struct drm_mode_create_dumb *args

提供给 struct drm_driver.dumb_create 的参数

描述

此函数要求驱动程序为其 VRAM MM 实例使用 drm_device.vram_mm

返回

成功时返回 0,否则返回负错误代码。

int drm_gem_vram_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state)

实现 struct drm_plane_helper_funcs.prepare_fb

参数

struct drm_plane *plane

一个 DRM 平面

struct drm_plane_state *new_state

该平面的新状态

描述

在平面更新期间,此函数设置平面的栅栏并将平面新帧缓冲区的 GEM VRAM 对象固定到 VRAM。调用 drm_gem_vram_plane_helper_cleanup_fb() 来取消固定它们。

返回

成功返回 0,否则返回一个负的 errno 代码。

void drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state)

实现 struct drm_plane_helper_funcs.cleanup_fb

参数

struct drm_plane *plane

一个 DRM 平面

struct drm_plane_state *old_state

该平面的旧状态

描述

在平面更新期间,此函数从 VRAM 中取消固定平面旧帧缓冲区的 GEM VRAM 对象。与 drm_gem_vram_plane_helper_prepare_fb() 相辅相成。

void drm_vram_mm_debugfs_init(struct drm_minor *minor)

注册 VRAM MM debugfs 文件。

参数

struct drm_minor *minor

drm 次设备。

int drmm_vram_helper_init(struct drm_device *dev, uint64_t vram_base, size_t vram_size)

初始化 struct drm_vram_mm 的设备实例

参数

struct drm_device *dev

DRM 设备

uint64_t vram_base

视频内存的基地址

size_t vram_size

视频内存的大小(以字节为单位)

描述

创建 struct drm_vram_mm 的一个新实例,并将其存储在结构体 drm_device.vram_mm 中。该实例是自动管理的,并在设备清理时进行清理。多次调用此函数将生成错误消息。

返回

成功返回 0,否则返回一个负的 errno 代码。

enum drm_mode_status drm_vram_helper_mode_valid(struct drm_device *dev, const struct drm_display_mode *mode)

测试显示模式的帧缓冲区是否适合可用的视频内存。

参数

struct drm_device *dev

DRM 设备

const struct drm_display_mode *mode

要测试的模式

描述

此函数测试是否有足够的视频内存可用于指定的显示模式。原子模式设置要求在驱逐活动帧缓冲区之前将指定的帧缓冲区导入到视频内存中。因此,任何帧缓冲区最多可能消耗可用 VRAM 的一半。不能使用需要更大帧缓冲区的显示模式,即使 CRTC 支持它们。每个帧缓冲区都被假定为具有 32 位颜色深度。

注意

该函数只能测试是否通常支持显示模式。如果有太多帧缓冲区固定到视频内存,则在实践中可能仍然无法使用显示模式。32 位颜色深度适合所有当前用例。必要时可以添加更灵活的测试。

返回

如果支持显示模式,则返回 MODE_OK,否则返回 enum drm_mode_status 类型的错误代码。

GEM TTM 辅助函数参考

此库为由 ttm 支持的 gem 对象提供辅助函数。

void drm_gem_ttm_print_info(struct drm_printer *p, unsigned int indent, const struct drm_gem_object *gem)

打印 ttm_buffer_object 的 debugfs 信息

参数

struct drm_printer *p

DRM 打印机

unsigned int indent

制表符缩进级别

const struct drm_gem_object *gem

GEM 对象

描述

此函数可以用作 drm_gem_object_funcs.print_info 回调。

int drm_gem_ttm_vmap(struct drm_gem_object *gem, struct iosys_map *map)

vmap ttm_buffer_object

参数

struct drm_gem_object *gem

GEM 对象。

struct iosys_map *map

[out] 返回 dma-buf 映射。

描述

使用 ttm_bo_vmap() 映射 GEM 对象。此函数可以用作 drm_gem_object_funcs.vmap 回调。

返回

成功返回 0,否则返回一个负的 errno 代码。

void drm_gem_ttm_vunmap(struct drm_gem_object *gem, struct iosys_map *map)

vunmap ttm_buffer_object

参数

struct drm_gem_object *gem

GEM 对象。

struct iosys_map *map

dma-buf 映射。

描述

使用 ttm_bo_vunmap() 取消映射 GEM 对象。此函数可以用作 drm_gem_object_funcs.vmap 回调。

int drm_gem_ttm_mmap(struct drm_gem_object *gem, struct vm_area_struct *vma)

mmap ttm_buffer_object

参数

struct drm_gem_object *gem

GEM 对象。

struct vm_area_struct *vma

vm 区域。

描述

此函数可以用作 drm_gem_object_funcs.mmap 回调。

int drm_gem_ttm_dumb_map_offset(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset)

实现 struct drm_driver.dumb_map_offset

参数

struct drm_file *file

DRM 文件指针。

struct drm_device *dev

DRM 设备。

uint32_t handle

GEM 句柄

uint64_t *offset

成功时返回映射的内存偏移量

描述

为基于 TTM 的 GEM 驱动程序提供 struct drm_driver.dumb_map_offset 的实现。TTM 在内部分配偏移量,并且 drm_gem_ttm_dumb_map_offset() 为哑缓冲区实现返回它。

请参阅 struct drm_driver.dumb_map_offset

返回

成功返回 0,否则返回一个负的 errno 代码。

VMA 偏移管理器

vma-manager 负责将任意驱动程序相关的内存区域映射到线性用户地址空间。它为调用者提供偏移量,然后可以在 drm-device 的地址空间中使用。它会注意不重叠区域,适当地调整它们的大小,并且不会因不一致的虚假 vm_pgoff 字段而混淆 mm-core。驱动程序不应将此用于 VMEM 中的对象放置。此管理器仅应用于管理到线性用户空间 VM 的映射。

我们使用 drm_mm 作为后端来管理对象分配。但是,它针对 alloc/free 调用进行了高度优化,而不是查找。因此,我们使用 rb-tree 来加速偏移量查找。

您不得在单个地址空间上使用多个偏移管理器。否则,mm-core 将无法拆除内存映射,因为 VM 将不再是线性的。

此偏移管理器使用基于页面的地址。也就是说,每个参数和返回代码(drm_vma_node_offset_addr() 除外)均以页数而不是字节数给出。这意味着,对象大小和偏移量必须始终按页对齐(如往常一样)。如果您想获取给定偏移量的有效的基于字节的用户空间地址,请参阅 drm_vma_node_offset_addr()

除了偏移量管理之外,vma 偏移管理器还处理访问管理。对于允许访问给定节点的每个打开文件上下文,您必须调用 drm_vma_node_allow()。否则,在具有节点偏移量的此打开文件上调用 mmap() 将失败,并显示 -EACCES。要再次撤销访问,请使用 drm_vma_node_revoke()。但是,如果需要,调用者负责销毁已经存在的映射。

struct drm_vma_offset_node *drm_vma_offset_exact_lookup_locked(struct drm_vma_offset_manager *mgr, unsigned long start, unsigned long pages)

按确切地址查找节点

参数

struct drm_vma_offset_manager *mgr

管理器对象

unsigned long start

起始地址(基于页面,而不是基于字节)

unsigned long pages

对象大小(基于页面)

描述

drm_vma_offset_lookup_locked() 相同,但不允许节点中的任何偏移量。它只返回具有给定起始地址的确切对象。

返回

在确切起始地址 start 处的节点。

void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)

锁定查找以供扩展的私有使用

参数

struct drm_vma_offset_manager *mgr

管理器对象

描述

锁定 VMA 管理器以进行扩展查找。在持有此锁时,只允许锁定 VMA 函数调用。所有其他上下文都被 VMA 阻止,直到通过 drm_vma_offset_unlock_lookup() 释放锁为止。

如果您需要在再次释放此锁之前获取 drm_vma_offset_lookup_locked() 返回的对象引用,请使用此方法。

此锁不得用于扩展查找之外的任何其他用途。在持有此锁时,不得调用任何其他 VMA 帮助程序。

注意

在持有此锁时,您处于原子上下文中!

void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)

解锁查找以供扩展的私有使用

参数

struct drm_vma_offset_manager *mgr

管理器对象

描述

释放查找锁。有关更多信息,请参阅 drm_vma_offset_lock_lookup()

void drm_vma_node_reset(struct drm_vma_offset_node *node)

初始化或重置节点对象

参数

struct drm_vma_offset_node *node

要初始化或重置的节点

描述

将节点重置为其初始状态。必须在将其与任何 VMA 偏移管理器一起使用之前调用此方法。

不得在已分配的节点上调用此方法,否则会发生内存泄漏。

unsigned long drm_vma_node_start(const struct drm_vma_offset_node *node)

返回基于页面的寻址的起始地址

参数

const struct drm_vma_offset_node *node

要检查的节点

描述

返回给定节点的起始地址。这可以用作 VMA 偏移管理器提供的线性 VM 空间的偏移量。请注意,这只能用于基于页面的寻址。如果您需要用户空间映射的适当偏移量,则必须应用“<< PAGE_SHIFT”或使用 drm_vma_node_offset_addr() 帮助程序。

返回

用于基于页面的寻址的 node 的起始地址。如果节点未分配偏移量,则为 0。

unsigned long drm_vma_node_size(struct drm_vma_offset_node *node)

返回大小(基于页面)

参数

struct drm_vma_offset_node *node

要检查的节点

描述

以页数形式返回给定节点的大小。这与传递给 drm_vma_offset_add() 的大小相同。如果未为节点分配任何偏移量,则为 0。

返回

node 的大小(以页数为单位)。如果节点未分配偏移量,则为 0。

__u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)

返回用户空间 mmaps 的经过清理的偏移量

参数

struct drm_vma_offset_node *node

链接的偏移量节点

描述

drm_vma_node_start() 相同,但返回的地址是有效的偏移量,可在 mmap() 期间用于用户空间映射。不得在未链接的节点上调用此方法。

返回

用于基于字节的寻址的 node 的偏移量。如果节点未分配对象,则为 0。

void drm_vma_node_unmap(struct drm_vma_offset_node *node, struct address_space *file_mapping)

取消映射偏移节点

参数

struct drm_vma_offset_node *node

偏移节点

struct address_space *file_mapping

要从中取消映射 node 的地址空间

描述

取消映射给定偏移节点的所有用户空间映射。 这些映射必须与 file_mapping 地址空间关联。 如果不存在偏移量,则不执行任何操作。

此调用是解锁的。 调用者必须保证不会同时在此节点上调用 drm_vma_offset_remove()

int drm_vma_node_verify_access(struct drm_vma_offset_node *node, struct drm_file *tag)

TTM 的访问验证助手

参数

struct drm_vma_offset_node *node

偏移节点

struct drm_file *tag

要检查的文件的标签

描述

这会检查是否已授予 tag 访问 node 的权限。 它与 drm_vma_node_is_allowed() 相同,但适合作为 TTM verify_access() 回调的直接助手。

返回

如果授予访问权限,则为 0,否则为 -EACCES。

void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, unsigned long page_offset, unsigned long size)

初始化新的偏移量管理器

参数

struct drm_vma_offset_manager *mgr

管理器对象

unsigned long page_offset

可用内存区域的偏移量(基于页)

unsigned long size

可用地址空间范围的大小(基于页)

描述

初始化新的偏移量管理器。管理器的可用偏移量和区域大小以 page_offsetsize 给出。两者都被解释为页码,而不是字节。

从管理器添加/删除节点会在内部锁定,并防止并发访问。但是,节点分配和销毁留给调用者。在调用 vma 管理器时,必须始终保证对给定节点进行引用。

void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)

销毁偏移量管理器

参数

struct drm_vma_offset_manager *mgr

管理器对象

描述

销毁先前通过 drm_vma_offset_manager_init() 创建的对象管理器。 调用者必须在销毁管理器之前删除所有已分配的节点。 否则,drm_mm 将拒绝释放请求的资源。

在此函数调用后,不得访问管理器。

struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr, unsigned long start, unsigned long pages)

在偏移空间中查找节点

参数

struct drm_vma_offset_manager *mgr

管理器对象

unsigned long start

对象的起始地址(基于页)

unsigned long pages

对象大小(基于页面)

描述

给定起始地址和对象大小,查找节点。 这将返回给定节点的 _最佳_ 匹配项。 也就是说,start 可能指向有效区域中的某个位置,并且只要节点跨越整个请求的区域(给定大小为 pages 的页数),就会返回给定节点。

请注意,在查找之前,必须使用 drm_vma_offset_lock_lookup() 获取 vma 偏移量管理器查找锁。 有关示例,请参见此处。 然后,可以使用它来实现使用 kref_get_unless_zero() 的弱引用查找。

drm_vma_offset_lock_lookup(mgr);
node = drm_vma_offset_lookup_locked(mgr);
if (node)
    kref_get_unless_zero(container_of(node, sth, entr));
drm_vma_offset_unlock_lookup(mgr);

示例

返回

如果找不到合适的节点,则返回 NULL。 否则,将返回最佳匹配项。 调用者有责任确保节点在调用者访问之前不会被销毁。

int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, struct drm_vma_offset_node *node, unsigned long pages)

向管理器添加偏移量节点

参数

struct drm_vma_offset_manager *mgr

管理器对象

struct drm_vma_offset_node *node

要添加的节点

unsigned long pages

用户空间可见的分配大小(以页数表示)

描述

向偏移量管理器添加节点。如果已添加该节点,则此操作不执行任何操作并返回 0。pages 是以页数表示的对象大小。在此调用成功后,您可以访问该节点的偏移量,直到再次将其删除。

如果此调用失败,则重试该操作或调用 drm_vma_offset_remove() 是安全的。但是,在这种情况下,不需要清理。

pages 不需要与要映射的底层内存对象的大小相同。它仅限制用户空间可以映射到其地址空间的大小。

返回

成功时为 0,失败时为负错误代码。

void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, struct drm_vma_offset_node *node)

从管理器中删除偏移量节点

参数

struct drm_vma_offset_manager *mgr

管理器对象

struct drm_vma_offset_node *node

要删除的节点

描述

从偏移量管理器中删除节点。如果之前未添加该节点,则此操作不执行任何操作。在此调用返回后,偏移量和大小将为 0,直到通过 drm_vma_offset_add() 再次分配新的偏移量。如果未分配偏移量,则 drm_vma_node_start()drm_vma_node_offset_addr() 等辅助函数将返回 0。

int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag)

将打开的文件添加到允许的用户列表

参数

struct drm_vma_offset_node *node

要修改的节点

struct drm_file *tag

要删除的文件的标签

描述

tag 添加到此节点允许的打开文件列表。 如果 tag 已在此列表上,则引用计数会增加。

允许的用户列表在 drm_vma_offset_add()drm_vma_offset_remove() 调用之间保留。 如果该节点当前未添加到任何偏移量管理器,您甚至可以调用它。

在销毁节点之前,您必须删除所有打开的文件,次数与添加它们的次数相同。 否则,您将泄漏内存。

这在内部锁定以防止并发访问。

返回

成功时为 0,内部失败(内存不足)时为负错误代码

int drm_vma_node_allow_once(struct drm_vma_offset_node *node, struct drm_file *tag)

将打开的文件添加到允许的用户列表

参数

struct drm_vma_offset_node *node

要修改的节点

struct drm_file *tag

要删除的文件的标签

描述

tag 添加到此节点允许的打开文件列表。

允许的用户列表在 drm_vma_offset_add()drm_vma_offset_remove() 调用之间保留。 如果该节点当前未添加到任何偏移量管理器,您甚至可以调用它。

drm_vma_node_allow() 不同,它不是引用计数的,因此在调用此函数后,drm_vma_node_revoke() 应该只被调用一次。

这在内部锁定以防止并发访问。

返回

成功时为 0,内部失败(内存不足)时为负错误代码

void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct drm_file *tag)

从允许的用户列表中移除打开的文件。

参数

struct drm_vma_offset_node *node

要修改的节点

struct drm_file *tag

要删除的文件的标签

描述

减少 node 上允许的打开文件列表中 tag 的引用计数。如果引用计数降至零,则从列表中删除 tag。对于 tag 上的每个 drm_vma_node_allow(),必须调用此函数一次。

这在内部锁定以防止并发访问。

如果 tag 不在列表中,则不执行任何操作。

bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, struct drm_file *tag)

检查是否授予了打开的文件访问权限。

参数

struct drm_vma_offset_node *node

要检查的节点。

struct drm_file *tag

要删除的文件的标签

描述

node 中搜索 tag 是否当前在允许的打开文件列表中(请参阅 drm_vma_node_allow())。

这在内部锁定以防止并发访问。

返回

如果 filp 在列表中,则为 true。

PRIME 缓冲区共享

PRIME 是 drm 中的跨设备缓冲区共享框架,最初是为 OPTIMUS 系列多 GPU 平台创建的。对于用户空间,PRIME 缓冲区是基于 dma-buf 的文件描述符。

概述和生命周期规则

与 GEM 全局名称类似,PRIME 文件描述符也用于在进程之间共享缓冲区对象。它们提供了额外的安全性:由于必须通过 UNIX 域套接字显式发送文件描述符才能在应用程序之间共享,因此它们不能像全局唯一的 GEM 名称那样被猜测出来。

支持 PRIME API 的驱动程序实现 drm_gem_object_funcs.export 和 drm_driver.gem_prime_import 钩子。驱动程序的 dma_buf_ops 实现都是单独导出的,供需要覆盖或重新实现其中一些的驱动程序使用。

GEM 驱动程序的引用计数

在导出时,dma_buf 保留对导出的缓冲区对象的引用,通常是 drm_gem_object。它在第一次调用 drm_gem_object_funcs.export 时在 PRIME_HANDLE_TO_FD IOCTL 中获取此引用,并将导出的 GEM 对象存储在 dma_buf.priv 字段中。当删除对 dma_buf 本身的最终引用时,并且调用其 dma_buf_ops.release 函数时,需要释放此引用。对于基于 GEM 的驱动程序,应使用 drm_gem_dmabuf_export() 导出 dma_buf,然后通过 drm_gem_dmabuf_release() 释放。

因此,引用链始终沿一个方向流动,避免循环:导入 GEM 对象 -> dma-buf -> 导出的 GEM bo。另一个复杂因素是导入和导出的查找缓存。需要这些来保证任何给定对象始终只有一个唯一的用户空间句柄。这是允许用户空间检测重复导入所必需的,因为如果给定的缓冲区对象被列出多次,某些 GEM 驱动程序确实会失败命令提交。 drm_prime_file_private 中的这些导入和导出缓存仅保留弱引用,该弱引用在释放相应的对象时会被清理。

自导入:如果用户空间使用 PRIME 作为 flink 的替代品,它将收到对其创建的 GEM 对象的 fd->handle 请求。驱动程序应检测到这种情况并从 dma-buf 私有数据返回底层对象。对于基于 GEM 的驱动程序,这已经在 drm_gem_prime_import() 中处理。

PRIME 辅助函数

驱动程序可以使用辅助函数 drm_gem_prime_export()drm_gem_prime_import(),通过更简单的 API 来实现 drm_gem_object_funcs.exportdrm_driver.gem_prime_import。这些函数根据一些较低级别的辅助函数实现 dma-buf 支持,这些辅助函数再次导出供驱动程序单独使用。

导出缓冲区

缓冲区的可选锁定在 drm_gem_map_attach()drm_gem_map_detach() 中的 dma-buf 附加和分离时处理。后备存储本身由 drm_gem_map_dma_buf()drm_gem_unmap_dma_buf() 处理,这依赖于 drm_gem_object_funcs.get_sg_table。如果未实现 drm_gem_object_funcs.get_sg_table,则拒绝导出到另一个设备。

对于内核内部访问,有 drm_gem_dmabuf_vmap()drm_gem_dmabuf_vunmap()。用户空间 mmap 支持由 drm_gem_dmabuf_mmap() 提供。

请注意,只有当底层后备存储完全一致且永久锁定,或者可以安全地无限期锁定时,才能使用这些导出助手。

FIXME:底层辅助函数的命名相当不一致。

导入缓冲区

使用 drm_gem_prime_import() 导入 dma-buf 依赖于 drm_driver.gem_prime_import_sg_table

请注意,与导出助手类似,这会永久锁定底层后备存储。这对于扫描输出是没问题的,但对于共享大量缓冲区进行渲染来说,不是最佳选择。

PRIME 函数参考

struct drm_prime_file_private

每个文件的 PRIME 跟踪。

定义:

struct drm_prime_file_private {
};

成员

描述

这仅包含 PRIME 核心代码使用的每个 struct drm_file 的内部 struct dma_buf 和句柄缓存。

struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, struct dma_buf_export_info *exp_info)

GEM 的 dma_buf 导出实现。

参数

struct drm_device *dev

导出的 dmabuf 的父设备。

struct dma_buf_export_info *exp_info

dma_buf_export() 使用的导出信息

描述

此函数封装了 dma_buf_export(),供使用 drm_gem_dmabuf_release() 的通用 GEM 驱动程序使用。除了调用 dma_buf_export() 之外,我们还会获取对 drm_device 和导出的 drm_gem_object 的引用(存储在 dma_buf_export_info.priv 中),该引用由 drm_gem_dmabuf_release() 释放。

返回新的 dmabuf。

void drm_gem_dmabuf_release(struct dma_buf *dma_buf)

GEM 的 dma_buf 释放实现

参数

struct dma_buf *dma_buf

要释放的缓冲区

描述

作为 PRIME 缓冲区导出的 dma_bufs 的通用释放函数。GEM 驱动程序必须在其 dma_buf_ops 结构中将其用作释放回调。drm_gem_dmabuf_release() 应该与 drm_gem_dmabuf_export() 一起使用。

int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle)

GEM 驱动程序的 PRIME 导入函数

参数

struct drm_device *dev

要导入的 drm_device

struct drm_file *file_priv

drm 文件私有结构

int prime_fd

应导入的 dma-buf 的 fd ID

uint32_t *handle

用于存储导入的缓冲区对象的句柄的指针

描述

这是 PRIME 导入函数,GEM 驱动程序必须强制使用此函数以确保底层 GEM 对象的正确生命周期管理。从 dma-buf 实际导入 GEM 对象是通过 drm_driver.gem_prime_import 驱动程序回调完成的。

成功时返回 0,失败时返回负错误代码。

struct dma_buf *drm_gem_prime_handle_to_dmabuf(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags)

GEM 驱动程序的 PRIME 导出函数

参数

struct drm_device *dev

从中导出缓冲区的 dev

struct drm_file *file_priv

drm 文件私有结构

uint32_t handle

要导出的缓冲区句柄

uint32_t flags

类似 DRM_CLOEXEC 的标志

描述

这是 PRIME 导出函数,GEM 驱动程序必须强制使用此函数以确保底层 GEM 对象的正确生命周期管理。从 GEM 对象实际导出到 dma-buf 是通过 drm_gem_object_funcs.export 回调完成的。

drm_gem_prime_handle_to_fd() 不同,它返回已创建的 struct dma_buf,而不将其附加到任何文件描述符。这两者之间的区别类似于 anon_inode_getfile()anon_inode_getfd() 之间的区别;如果需要任何清理,则插入到描述符表中是无法撤消的,因此只有在您经过最后一个失败退出并且唯一剩下的就是将新的文件描述符传递给用户空间时,才应使用返回描述符的变体。当您只需要对象本身或需要执行其他可能失败的操作时,请改用此对象。

int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd)

GEM 驱动程序的 PRIME 导出函数

参数

struct drm_device *dev

从中导出缓冲区的 dev

struct drm_file *file_priv

drm 文件私有结构

uint32_t handle

要导出的缓冲区句柄

uint32_t flags

类似 DRM_CLOEXEC 的标志

int *prime_fd

用于存储创建的 dma-buf 的 fd ID 的指针

描述

这是 PRIME 导出函数,GEM 驱动程序必须强制使用此函数以确保底层 GEM 对象的正确生命周期管理。从 GEM 对象实际导出到 dma-buf 是通过 drm_gem_object_funcs.export 回调完成的。

int drm_gem_map_attach(struct dma_buf *dma_buf, struct dma_buf_attachment *attach)

GEM 的 dma_buf 附加实现

参数

struct dma_buf *dma_buf

要附加设备的缓冲区

struct dma_buf_attachment *attach

缓冲区附加数据

描述

为设备特定处理调用 drm_gem_object_funcs.pin。这可以用作 dma_buf_ops.attach 回调。必须与 drm_gem_map_detach() 一起使用。

成功时返回 0,失败时返回负错误代码。

void drm_gem_map_detach(struct dma_buf *dma_buf, struct dma_buf_attachment *attach)

GEM 的 dma_buf 分离实现

参数

struct dma_buf *dma_buf

要分离的缓冲区

struct dma_buf_attachment *attach

要分离的附件

描述

为设备特定处理调用 drm_gem_object_funcs.pin。从 drm_gem_map_attach() 清理 dma_buf_attachment。这可以用作 dma_buf_ops.detach 回调。

struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, enum dma_data_direction dir)

GEM 的 map_dma_buf 实现

参数

struct dma_buf_attachment *attach

要返回散列表的附件

enum dma_data_direction dir

DMA 传输方向

描述

调用 drm_gem_object_funcs.get_sg_table,然后映射散列表。这可以用作 dma_buf_ops.map_dma_buf 回调。应与 drm_gem_unmap_dma_buf() 一起使用。

返回

包含要返回的散列表的 sg_table;出错时返回 ERR_PTR。如果被信号中断,可能会返回 -EINTR。

void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, struct sg_table *sgt, enum dma_data_direction dir)

GEM 的 unmap_dma_buf 实现

参数

struct dma_buf_attachment *attach

要从中取消映射缓冲区的附件

struct sg_table *sgt

要取消映射的缓冲区的散列表信息

enum dma_data_direction dir

DMA 传输方向

描述

这可以用作 dma_buf_ops.unmap_dma_buf 回调。

int drm_gem_dmabuf_vmap(struct dma_buf *dma_buf, struct iosys_map *map)

GEM 的 dma_buf vmap 实现

参数

struct dma_buf *dma_buf

要映射的缓冲区

struct iosys_map *map

缓冲区的虚拟地址

描述

设置内核虚拟映射。这可以用作 dma_buf_ops.vmap 回调。调用 drm_gem_object_funcs.vmap 进行设备特定的处理。内核虚拟地址在 map 中返回。

成功时返回 0,否则返回负的 errno 代码。

void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, struct iosys_map *map)

GEM 的 dma_buf vunmap 实现

参数

struct dma_buf *dma_buf

要取消映射的缓冲区

struct iosys_map *map

缓冲区的虚拟地址

描述

释放内核虚拟映射。这可以用作 dma_buf_ops.vunmap 回调。调用 drm_gem_object_funcs.vunmap 进行设备特定的处理。

int drm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)

GEM 驱动程序的 PRIME mmap 函数

参数

struct drm_gem_object *obj

GEM 对象

struct vm_area_struct *vma

虚拟地址范围

描述

此函数使用与 DRM fd 上常规 GEM 缓冲区映射相同的代码路径,为 PRIME 导出的缓冲区设置用户空间映射。伪 GEM 偏移量添加到 vma->vm_pgoff,并调用 drm_driver->fops->mmap 来设置映射。

int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)

GEM 的 dma_buf mmap 实现

参数

struct dma_buf *dma_buf

要映射的缓冲区

struct vm_area_struct *vma

虚拟地址范围

描述

为缓冲区提供内存映射。这可以用作 dma_buf_ops.mmap 回调。它只是转发到 drm_gem_prime_mmap()

成功时返回 0,失败时返回负错误代码。

struct sg_table *drm_prime_pages_to_sg(struct drm_device *dev, struct page **pages, unsigned int nr_pages)

将页面数组转换为 sg 列表

参数

struct drm_device *dev

DRM 设备

struct page **pages

指向要转换的页面指针数组的指针

unsigned int nr_pages

页面向量的长度

描述

此帮助程序从一组页面创建 sg 表对象,驱动程序负责将页面映射到导入者的地址空间,以便与 dma_buf 本身一起使用。

这对于实现 drm_gem_object_funcs.get_sg_table 非常有用。

unsigned long drm_prime_get_contiguous_size(struct sg_table *sgt)

返回缓冲区的连续大小

参数

struct sg_table *sgt

描述要检查的缓冲区的 sg_table

描述

此帮助程序计算由提供的 sg_table 描述的缓冲区在 DMA 地址空间中的连续大小。

这对于实现 drm_gem_object_funcs.gem_prime_import_sg_table 非常有用。

struct dma_buf *drm_gem_prime_export(struct drm_gem_object *obj, int flags)

导出回调的帮助程序库实现

参数

struct drm_gem_object *obj

要导出的 GEM 对象

int flags

DRM_CLOEXEC 和 DRM_RDWR 等标志

描述

这是使用 PRIME 帮助程序的 GEM 驱动程序的 drm_gem_object_funcs.export 函数的实现。它在 drm_gem_prime_handle_to_fd() 中用作默认值。

struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev, struct dma_buf *dma_buf, struct device *attach_dev)

导入回调的核心实现

参数

struct drm_device *dev

要导入的 drm_device

struct dma_buf *dma_buf

要导入的 dma-buf 对象

struct device *attach_dev

用于 dma_buf 附加的 struct device

描述

这是 drm_gem_prime_import() 的核心。它旨在被希望使用与 drm_device.dev 不同的设备结构的驱动程序调用,以便通过 dma_buf 进行连接。此函数内部调用 drm_driver.gem_prime_import_sg_table

驱动程序必须安排在他们的 drm_gem_object_funcs.free 钩子中使用此函数时调用 drm_prime_gem_destroy()

struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf)

导入回调的辅助库实现

参数

struct drm_device *dev

要导入的 drm_device

struct dma_buf *dma_buf

要导入的 dma-buf 对象

描述

这是使用 PRIME 辅助函数的 GEM 驱动程序的 gem_prime_import 函数的实现。驱动程序可以将此用作它们的 drm_driver.gem_prime_import 实现。它在 drm_gem_prime_fd_to_handle() 中用作默认实现。

驱动程序必须安排在他们的 drm_gem_object_funcs.free 钩子中使用此函数时调用 drm_prime_gem_destroy()

int drm_prime_sg_to_page_array(struct sg_table *sgt, struct page **pages, int max_entries)

将 sg 表转换为页数组

参数

struct sg_table *sgt

要转换的散列表

struct page **pages

用于存储页面的页指针数组

int max_entries

传入数组的大小

描述

将 sg 表导出为页面数组。

此函数已弃用,强烈建议不要使用。页数组仅对页面错误有用,如果导出驱动程序未处理,这些错误可能会损坏 struct page 中的字段。

int drm_prime_sg_to_dma_addr_array(struct sg_table *sgt, dma_addr_t *addrs, int max_entries)

将 sg 表转换为 dma 地址数组

参数

struct sg_table *sgt

要转换的散列表

dma_addr_t *addrs

用于存储每个页面的 dma 总线地址的数组

int max_entries

传入的两个数组的大小

描述

将 sg 表导出为地址数组。

驱动程序应在它们的 drm_driver.gem_prime_import_sg_table 实现中使用此函数。

void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg)

用于清理 PRIME 导入的 GEM 对象的辅助函数

参数

struct drm_gem_object *obj

从 dma-buf 创建的 GEM 对象

struct sg_table *sg

在导入时锁定的 sg 表

描述

这是 GEM 驱动程序在使用 drm_gem_prime_import()drm_gem_prime_import_dev() 导入 dma-bufs 时需要调用的清理函数。

DRM MM 范围分配器

概述

drm_mm 提供了一个简单的范围分配器。驱动程序可以自由地使用来自 linux 内核的资源分配器,如果它适合它们的话,drm_mm 的优点是它在 DRM 内核中。这意味着对于 GPU 的一些更疯狂的特殊用途需求,它更容易扩展。

主要的数据结构是 drm_mm,分配在 drm_mm_node 中进行跟踪。驱动程序可以自由地将它们中的任何一个嵌入到它们自己合适的数据结构中。drm_mm 本身不会进行任何自己的内存分配,因此如果驱动程序选择不嵌入节点,它们仍然需要自己分配它们。

范围分配器还支持预分配块的预留。这对于接管固件的初始模式设置配置非常有用,在这种情况下,需要创建一个与固件的扫描输出目标完全匹配的对象。只要范围仍然空闲,它就可以在初始化分配器后的任何时间插入,这有助于避免驱动程序加载序列中的循环依赖。

drm_mm 维护一个最近释放的孔的堆栈,从所有简单的结构来看,这似乎是一种相当不错的聚类分配方法,可以避免过多的碎片。这意味着空闲空间搜索是 O(num_holes)。鉴于 drm_mm 支持的所有花哨的功能,更好的方法会相当复杂,而且由于 gfx 抖动是一个相当陡峭的悬崖,所以不是一个真正的问题。再次删除节点是 O(1)。

drm_mm 支持一些功能:可以提供对齐和范围限制。此外,每个 drm_mm_node 都有一个颜色值(只是一个不透明的无符号长整数),结合驱动程序回调,可以用来实现复杂的放置限制。i915 DRM 驱动程序使用此功能在图形 TT 中不兼容的缓存域之间实现保护页。

搜索和分配支持两种行为:自下而上和自上而下。默认值为自下而上。如果内存区域具有不同的限制,或者只是为了减少碎片,可以使用自上而下的分配。

最后,提供了用于遍历所有节点和所有孔的迭代辅助函数,以及一些用于调试的基本分配器转储器。

请注意,此范围分配器不是线程安全的,驱动程序需要使用自己的锁来保护修改。这背后的想法是,对于一个完整的内存管理器,无论如何都需要保护额外的数据,因此内部锁定将是完全多余的。

LRU 扫描/驱逐支持

通常,GPU 需要为给定对象进行连续分配。当驱逐对象为新对象腾出空间时,当我们简单地开始从 LRU 的尾部选择所有对象直到有一个合适的孔时,这不是最有效的:特别是对于大的对象或具有特殊分配约束的节点,很可能会不必要地驱逐许多(较小的)对象。

DRM 范围分配器通过扫描接口支持此用例。首先,需要使用 drm_mm_scan_init()drm_mm_scan_init_with_range() 初始化扫描操作。驱动程序将对象添加到列表中,可能通过遍历 LRU 列表,但这可以自由实现。使用 drm_mm_scan_add_block() 添加驱逐候选项,直到找到合适的孔或没有更多可驱逐的对象。驱逐列表元数据在 struct drm_mm_scan 中进行跟踪。

驱动程序必须以完全相反的顺序再次遍历所有对象以恢复分配器状态。请注意,当分配器处于扫描模式时,不允许进行其他操作。

最后,驱动程序会驱逐扫描中选择的所有对象(drm_mm_scan_remove_block() 报告为 true),以及颜色调整后任何重叠的节点(drm_mm_scan_color_evict())。添加和删除对象是 O(1),并且由于释放节点也是 O(1),因此整体复杂性是 O(scanned_objects)。所以像在扫描操作开始之前需要遍历的空闲堆栈一样,这在对象数量上是线性的。似乎没有太大的损害。

DRM MM 范围分配器函数参考

enum drm_mm_insert_mode

控制搜索和分配行为

常量

DRM_MM_INSERT_BEST

搜索适合所需节点的最小空洞(在搜索范围内)。

从找到的空洞底部分配节点。

DRM_MM_INSERT_LOW

搜索适合所需节点的最低空洞(地址最接近 0,在搜索范围内)。

从找到的空洞底部分配节点。

DRM_MM_INSERT_HIGH

搜索适合所需节点的最高空洞(地址最接近 U64_MAX,在搜索范围内)。

从找到的空洞的顶部分配节点。为节点指定的对齐方式应用于节点的基址(drm_mm_node.start)。

DRM_MM_INSERT_EVICT

搜索最近驱逐的适合所需节点的空洞(在搜索范围内)。这适用于在执行驱逐扫描(请参阅drm_mm_scan_init())并删除所选节点以形成空洞之后立即使用。

从找到的空洞底部分配节点。

DRM_MM_INSERT_ONCE

仅检查第一个空洞是否合适,否则立即报告 -ENOSPC,而不是检查每个空洞直到找到合适的空洞。只能与另一种搜索方法(例如 DRM_MM_INSERT_HIGH 或 DRM_MM_INSERT_LOW)结合使用。

DRM_MM_INSERT_HIGHEST

仅检查最高的空洞(地址最大的空洞),并将节点插入到空洞的顶部,如果空洞不合适则报告 -ENOSPC。

不搜索所有空洞。

DRM_MM_INSERT_LOWEST

仅检查最低的空洞(地址最小的空洞),并将节点插入到空洞的底部,如果空洞不合适则报告 -ENOSPC。

不搜索所有空洞。

描述

struct drm_mm 范围管理器支持使用多个搜索树来查找合适的模式。这些树按大小、地址和最近的驱逐顺序组织。这允许用户查找要重用的最小空洞、要重用的最低或最高地址,或者只是重用最适合的最近驱逐。当从空洞中分配 drm_mm_node 时,drm_mm_insert_mode 还决定是分配最低匹配地址还是最高匹配地址。

struct drm_mm_node

DRM 分配器中已分配的块

定义:

struct drm_mm_node {
    unsigned long color;
    u64 start;
    u64 size;
};

成员

颜色

不透明的驱动程序私有标签。

start

已分配块的起始地址。

size

已分配块的大小。

描述

这表示 drm_mm 分配器中已分配的块。除了使用 drm_mm_reserve_node() 插入的预留节点外,该结构完全不透明,只能通过提供的函数访问。由于这些节点的分配完全由驱动程序处理,因此可以嵌入它们。

struct drm_mm

DRM 分配器

定义:

struct drm_mm {
    void (*color_adjust)(const struct drm_mm_node *node,unsigned long color, u64 *start, u64 *end);
};

成员

color_adjust

可选的驱动程序回调,用于进一步限制空洞。 node 参数指向包含将从中分配块的空洞的节点(请参阅 drm_mm_hole_follows() 和相关函数)。其他参数是要分配的块的大小。驱动程序可以根据需要调整起始和结束位置,例如插入保护页。

描述

DRM 范围分配器,具有一些专门用于管理 GPU 内存的特殊功能。除了 color_adjust 回调之外,该结构完全不透明,只能通过提供的函数和宏访问。此结构可以嵌入到更大的驱动程序结构中。

struct drm_mm_scan

DRM 分配器驱逐名册数据

定义:

struct drm_mm_scan {
};

成员

描述

此结构跟踪使用 drm_mm_scan_init() 设置的驱逐名册所需的数据,并与 drm_mm_scan_add_block()drm_mm_scan_remove_block() 一起使用。该结构完全不透明,只能通过提供的函数和宏访问。它旨在由驱动程序在堆栈上临时分配。

bool drm_mm_node_allocated(const struct drm_mm_node *node)

检查节点是否已分配

参数

const struct drm_mm_node *node

要检查的 drm_mm_node

描述

驱动程序需要在将节点与 drm_mm 范围管理器一起使用之前清除该节点。

驱动程序应使用此助手来正确封装 drm_mm 内部结构。

返回

如果 node 已分配,则为 True。

bool drm_mm_initialized(const struct drm_mm *mm)

检查分配器是否已初始化

参数

const struct drm_mm *mm

要检查的 drm_mm

描述

如果驱动程序要使用此函数,则应在初始化之前清除 struct drm_mm

驱动程序应使用此助手来正确封装 drm_mm 内部结构。

返回

如果 mm 已初始化,则为 True。

bool drm_mm_hole_follows(const struct drm_mm_node *node)

检查此节点后是否有空洞

参数

const struct drm_mm_node *node

要检查的 drm_mm_node

描述

空洞使用 drm_mm_node 的尾部嵌入到 drm_mm 中。如果您想知道此特定节点后是否有空洞,请查询此函数。另请参阅 drm_mm_hole_node_start()drm_mm_hole_node_end()

返回

如果 node 后有空洞,则为 True。

u64 drm_mm_hole_node_start(const struct drm_mm_node *hole_node)

计算 node 之后的空洞的起始位置

参数

const struct drm_mm_node *hole_node

隐式跟踪后续空洞的 drm_mm_node

描述

这对于特定于驱动程序的调试转储器很有用。否则,驱动程序不应自行检查空洞。驱动程序必须首先通过查看 drm_mm_hole_follows() 来检查空洞是否确实存在。

返回

后续空洞的起始位置。

u64 drm_mm_hole_node_end(const struct drm_mm_node *hole_node)

计算 node 之后的空洞的结束位置

参数

const struct drm_mm_node *hole_node

隐式跟踪后续空洞的 drm_mm_node

描述

这对于特定于驱动程序的调试转储器很有用。否则,驱动程序不应自行检查空洞。驱动程序必须首先通过查看 drm_mm_hole_follows() 来检查空洞是否确实存在。

返回

后续空洞的结束位置。

drm_mm_nodes

drm_mm_nodes (mm)

drm_mm 范围管理器下的节点列表

参数

mm

struct drm_mm 范围管理器

描述

由于 drm_mm 范围管理器将其 node_list 深藏于其结构中,因此提取它看起来很痛苦且重复。预计不会在 drm_mm_for_each_node() 宏和类似的内部函数之外使用。

返回

节点列表,可能为空。

drm_mm_for_each_node

drm_mm_for_each_node (entry, mm)

用于遍历所有已分配节点的迭代器

参数

entry

在每个迭代步骤中分配到的 struct drm_mm_node

mm

遍历drm_mm分配器的节点

描述

此迭代器遍历范围分配器中的所有节点。它使用 list_for_each() 实现,因此不能防止元素的删除。

drm_mm_for_each_node_safe

drm_mm_for_each_node_safe (entry, next, mm)

用于遍历所有已分配节点的迭代器

参数

entry

在每个迭代步骤中分配到的 struct drm_mm_node

next

用于存储下一步的 struct drm_mm_node

mm

遍历drm_mm分配器的节点

描述

此迭代器遍历范围分配器中的所有节点。它使用 list_for_each_safe() 实现,因此可以防止元素的删除。

drm_mm_for_each_hole

drm_mm_for_each_hole (pos, mm, hole_start, hole_end)

遍历所有空闲区域的迭代器

参数

pos

内部用于跟踪进度的 drm_mm_node

mm

遍历drm_mm分配器的节点

hole_start

用于在每次迭代中分配空闲区域起点的 ulong 变量

hole_end

用于在每次迭代中分配空闲区域终点的 ulong 变量

描述

此迭代器遍历范围分配器中的所有空闲区域。它使用 list_for_each() 实现,因此不能防止元素的删除。 **entry** 在内部使用,不会反映第一个空闲区域的真实 drm_mm_node。因此,此迭代器的用户可能无法访问它。

实现注意:我们需要内联 list_for_each_entry,以便能够在每次迭代中设置 hole_start 和 hole_end,同时保持宏的健全性。

int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color, enum drm_mm_insert_mode mode)

搜索空间并插入 node

参数

struct drm_mm *mm

从中分配的 drm_mm

struct drm_mm_node *node

要插入的预分配节点

u64 size

分配的大小

u64 alignment

分配的对齐方式

unsigned long color

用于此节点的不透明标记值

enum drm_mm_insert_mode mode

微调分配搜索和放置

描述

这是 drm_mm_insert_node_in_range() 的简化版本,没有应用范围限制。

预分配的节点必须清零。

返回

成功返回 0,如果没有合适的空闲区域则返回 -ENOSPC。

int drm_mm_insert_node(struct drm_mm *mm, struct drm_mm_node *node, u64 size)

搜索空间并插入 node

参数

struct drm_mm *mm

从中分配的 drm_mm

struct drm_mm_node *node

要插入的预分配节点

u64 size

分配的大小

描述

这是 drm_mm_insert_node_generic() 的简化版本,其中 color 设置为 0。

预分配的节点必须清零。

返回

成功返回 0,如果没有合适的空闲区域则返回 -ENOSPC。

bool drm_mm_clean(const struct drm_mm *mm)

检查分配器是否为空

参数

const struct drm_mm *mm

要检查的 drm_mm 分配器

返回

如果分配器完全空闲则返回 True,如果其中仍然分配了节点则返回 false。

drm_mm_for_each_node_in_range

drm_mm_for_each_node_in_range (node__, mm__, start__, end__)

遍历已分配节点范围的迭代器

参数

node__

在每个迭代步骤中分配的 drm_mm_node 结构

mm__

要遍历的 drm_mm 分配器

start__

起始偏移量,第一个节点将与此重叠

end__

结束偏移量,最后一个节点将在此之前开始(但可能重叠)

描述

此迭代器遍历范围分配器中位于 startend 之间的所有节点。它的实现类似于 list_for_each(),但使用内部区间树来加速对起始节点的搜索,因此不能防止元素的删除。它假设 end 在 drm_mm 分配器的范围内(或作为其上限)。如果 [start, end] 超出 drm_mm 的范围,则迭代器可能会遍历特殊的 _未分配的_ drm_mm.head_node,甚至可能会无限期地继续下去。

void drm_mm_scan_init(struct drm_mm_scan *scan, struct drm_mm *mm, u64 size, u64 alignment, unsigned long color, enum drm_mm_insert_mode mode)

初始化 LRU 扫描

参数

struct drm_mm_scan *scan

扫描状态

struct drm_mm *mm

要扫描的 drm_mm

u64 size

分配的大小

u64 alignment

分配的对齐方式

unsigned long color

用于分配的不透明标记值

enum drm_mm_insert_mode mode

微调分配搜索和放置

描述

这是 drm_mm_scan_init_with_range() 的简化版本,没有应用范围限制。

这只是使用所需空闲区域的参数设置扫描例程。

警告:只要扫描列表不为空,就不允许进行除向/从扫描列表中添加/删除节点之外的任何其他操作。

int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)

插入预初始化的节点

参数

struct drm_mm *mm

要将 node 插入到的 drm_mm 分配器

struct drm_mm_node *node

要插入的 drm_mm_node

描述

此函数将已设置的 drm_mm_node 插入到分配器中,这意味着起点、大小和颜色必须由调用者设置。所有其他字段必须清零。这对于初始化具有预分配对象的分配器很有用,这些预分配对象必须在设置范围分配器之前进行设置,例如,在接管固件帧缓冲区时。

返回

成功返回 0,如果 node 所在位置没有空闲区域则返回 -ENOSPC。

int drm_mm_insert_node_in_range(struct drm_mm *const mm, struct drm_mm_node *const node, u64 size, u64 alignment, unsigned long color, u64 range_start, u64 range_end, enum drm_mm_insert_mode mode)

在指定范围内搜索空间并插入 node

参数

struct drm_mm * const mm

从中分配的 drm_mm

struct drm_mm_node * const node

要插入的预分配节点

u64 size

分配的大小

u64 alignment

分配的对齐方式

unsigned long color

用于此节点的不透明标记值

u64 range_start

此节点的允许范围的起始位置

u64 range_end

此节点的允许范围的结束位置

enum drm_mm_insert_mode mode

微调分配搜索和放置

描述

预先分配的节点必须清零。

返回

成功返回 0,如果没有合适的空闲区域则返回 -ENOSPC。

void drm_mm_remove_node(struct drm_mm_node *node)

从分配器中移除一个内存节点。

参数

struct drm_mm_node *node

要移除的 drm_mm_node

描述

这只是从其 drm_mm 分配器中移除一个节点。该节点不需要在重新插入到此分配器或任何其他 drm_mm 分配器之前再次清除。在未分配的节点上调用此函数是错误的。

void drm_mm_scan_init_with_range(struct drm_mm_scan *scan, struct drm_mm *mm, u64 size, u64 alignment, unsigned long color, u64 start, u64 end, enum drm_mm_insert_mode mode)

初始化范围受限的 LRU 扫描

参数

struct drm_mm_scan *scan

扫描状态

struct drm_mm *mm

要扫描的 drm_mm

u64 size

分配的大小

u64 alignment

分配的对齐方式

unsigned long color

用于分配的不透明标记值

u64 start

分配的允许范围的起始位置

u64 end

分配的允许范围的结束位置

enum drm_mm_insert_mode mode

微调分配搜索和放置

描述

这只是使用所需空闲区域的参数设置扫描例程。

警告:只要扫描列表不为空,就不允许进行除向/从扫描列表中添加/删除节点之外的任何其他操作。

bool drm_mm_scan_add_block(struct drm_mm_scan *scan, struct drm_mm_node *node)

将节点添加到扫描列表

参数

struct drm_mm_scan *scan

活动的 drm_mm 扫描器

struct drm_mm_node *node

要添加的 drm_mm_node

描述

将一个节点添加到扫描列表中,该节点可能会被释放以腾出所需空洞的空间。

返回

如果已找到空洞,则为 True,否则为 false。

bool drm_mm_scan_remove_block(struct drm_mm_scan *scan, struct drm_mm_node *node)

从扫描列表中移除一个节点

参数

struct drm_mm_scan *scan

活动的 drm_mm 扫描器

struct drm_mm_node *node

要移除的 drm_mm_node

描述

节点必须按照它们添加到扫描列表中的完全相反的顺序移除(例如,使用 list_add() 添加它们,然后使用 list_for_each() 遍历该驱逐列表来移除),否则内存管理器的内部状态将损坏。

当扫描列表为空时,可以释放选定的内存节点。紧接着调用 drm_mm_insert_node_in_range_generic() 或该函数的简化版本之一,且 !DRM_MM_SEARCH_BEST 时,将返回刚刚释放的块(因为它位于 free_stack 列表的顶部)。

返回

如果应该驱逐此块,则为 True,否则为 false。当未找到空洞时,将始终返回 false。

struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan)

驱逐空洞两侧的重叠节点

参数

struct drm_mm_scan *scan

具有目标空洞的 drm_mm 扫描

描述

在完成驱逐扫描并删除选定的节点后,如果正在使用 mm.color_adjust,我们可能需要从目标空洞的任一侧删除更多节点。

返回

要驱逐的节点,如果没有重叠的节点,则为 NULL。

void drm_mm_init(struct drm_mm *mm, u64 start, u64 size)

初始化一个 drm-mm 分配器

参数

struct drm_mm *mm

要初始化的 drm_mm 结构

u64 start

mm 管理的范围的起始位置

u64 size

mm 管理的范围的结束位置

描述

请注意,在调用此函数之前,必须将 mm 清零。

void drm_mm_takedown(struct drm_mm *mm)

清理一个 drm_mm 分配器

参数

struct drm_mm *mm

要清理的 drm_mm 分配器

描述

请注意,在未清理的分配器上调用此函数是错误的。

void drm_mm_print(const struct drm_mm *mm, struct drm_printer *p)

打印分配器状态

参数

const struct drm_mm *mm

要打印的 drm_mm 分配器

struct drm_printer *p

要使用的 DRM 打印机

DRM GPUVM

概述

DRM GPU VA 管理器,由 struct drm_gpuvm 表示,跟踪 GPU 的虚拟地址 (VA) 空间,并管理由 drm_gpuva 对象表示的相应虚拟映射。它还跟踪映射的后备 drm_gem_object 缓冲区。

drm_gem_object 缓冲区维护一个 drm_gpuva 对象列表,表示使用此 drm_gem_object 作为后备缓冲区的现有的所有 GPU VA 映射。

GPU VA 可以标记为稀疏,这样驱动程序可以使用 GPU VA 来跟踪稀疏 PTE,以支持 Vulkan“稀疏资源”。

GPU VA 管理器内部使用 rb-树来管理 GPU 虚拟地址空间中的 drm_gpuva 映射。

drm_gpuvm 结构体包含一个特殊的 drm_gpuva,表示内核保留的 VA 空间部分。此节点与 GPU VA 管理器实例一起初始化,并在 GPU VA 管理器销毁时删除。

在典型的应用程序中,驱动程序会将 struct drm_gpuvmstruct drm_gpuva 嵌入到它们自己的特定于驱动程序的结构中,它本身不会有任何内存分配,也不会有 drm_gpuva 条目的内存分配。

drm_gpuvm 中存储 drm_gpuvas 所需的数据结构已经包含在 struct drm_gpuva 中。因此,为了从 dma-fence 信号临界区内插入 drm_gpuva 条目,预先分配 drm_gpuva 结构体就足够了。

单个 VM 私有的 drm_gem_objects 可以共享一个公共的 dma_resv,以提高锁定效率(例如,使用 drm_exec)。为此,驱动程序必须将一个 drm_gem_object 传递给 drm_gpuvm_init(),在下文中称为“resv 对象”,它充当 GPUVM 共享的 dma_resv 的容器。此 resv 对象可以是驱动程序特定的 drm_gem_object,例如包含根页表的 drm_gem_object,但它也可以是一个“虚拟”对象,可以使用 drm_gpuvm_resv_object_alloc() 进行分配。

为了连接一个 struct drm_gpuva 及其后备 drm_gem_object,每个 drm_gem_object 都维护一个 drm_gpuvm_bo 结构体的列表,并且每个 drm_gpuvm_bo 都包含一个 drm_gpuva 结构体的列表。

drm_gpuvm_bo 是表示 drm_gpuvmdrm_gem_object 组合的抽象。每个这样的组合都应该是唯一的。API 通过 drm_gpuvm_bo_obtain()drm_gpuvm_bo_obtain_prealloc() 确保了这一点,它们首先查找相应的 drm_gem_objectdrm_gpuvm_bos 列表,以查找此特定组合的现有实例。如果不存在,则会创建一个新实例并将其链接到 drm_gem_object

drm_gpuvm_bo 结构体,由于对于给定的 drm_gpuvm 是唯一的,也被用作 drm_gpuvm 的外部和已驱逐对象列表的条目。维护这些列表是为了加速 dma-resv 锁的锁定以及在 drm_gpuvm 中绑定的已驱逐对象的验证。例如,可以通过调用 drm_gpuvm_exec_lock() 来锁定给定 drm_gpuvm 的所有 drm_gem_objectdma_resv。锁定后,驱动程序可以调用 drm_gpuvm_validate() 来验证所有已驱逐的 drm_gem_objects。也可以通过将相应的参数提供给 drm_gpuvm_exec_lock() 以及在利用 drm_gpuvm_prepare_range()drm_gpuvm_prepare_objects() 等辅助函数的同时打开代码 drm_exec 循环来锁定其他 drm_gem_objects

当绑定的 drm_gem_objectdma_resv 结构与 drm_gpuvm 的公共 dma_resv 结构不同时,每个绑定的 drm_gem_object 都被视为外部对象。

拆分和合并

除了管理和表示 GPU VA 空间的能力之外,GPU VA 管理器还提供了功能,让 drm_gpuvm 计算一系列操作以满足给定的映射或取消映射请求。

因此,DRM GPU VA 管理器提供了一种算法,该算法实现了现有 GPU VA 映射与请求映射或取消映射的映射的拆分和合并。Vulkan API 需要此功能来实现 Vulkan“稀疏内存绑定” - 驱动程序 UAPI 通常将其称为 VM BIND。

驱动程序可以调用 drm_gpuvm_sm_map() 来接收一系列回调,其中包含给定新请求的映射的映射、取消映射和重新映射操作。回调序列表示为了将新映射干净地集成到 GPU VA 空间的当前状态中而要执行的操作集。

根据新的 GPU VA 映射与 GPU VA 空间中现有映射的相交方式,drm_gpuvm_ops 回调包含任意数量的取消映射操作、最多两个重新映射操作和一个映射操作。如果没有操作,则调用者可能不会收到任何回调,例如,如果请求的映射已经以完全相同的方式存在。

单个映射操作表示调用者请求的原始映射操作。

drm_gpuva_op_unmap 包含一个 ‘keep’ 字段,该字段指示要取消映射的 drm_gpuva 在物理上是否与原始映射请求连续。可选地,如果设置了 ‘keep’,驱动程序可以保留此 drm_gpuva 的实际页表条目,仅添加缺少的页表条目,并相应地更新 drm_gpuvm 的视图。

驱动程序也可以对重新映射操作执行相同的优化,即增量页表更新。这是可行的,因为 drm_gpuva_op_remap 由一个取消映射操作和一个或两个映射操作组成,因此驱动程序可以相应地推导出页表更新增量。

请注意,最多只能拆分两个现有映射,一个在新映射的开头,另一个在新映射的结尾,因此最多只有两个重新映射操作。

类似于 drm_gpuvm_sm_map()drm_gpuvm_sm_unmap() 使用 drm_gpuvm_ops 回调到驱动程序中,以取消映射 GPU VA 空间的一部分范围。此函数背后的逻辑要简单得多:对于给定范围内的所有现有映射,将创建取消映射操作。对于仅部分位于给定范围内的映射,将创建重新映射操作,以便将这些映射拆分并部分重新映射。

作为 drm_gpuvm_sm_map()drm_gpuvm_sm_unmap() 的替代方案,可以使用 drm_gpuvm_sm_map_ops_create()drm_gpuvm_sm_unmap_ops_create() 直接获取 struct drm_gpuva_ops 的实例,其中包含 drm_gpuva_op 的列表,可以使用 drm_gpuva_for_each_op() 对其进行迭代。此列表包含 drm_gpuva_ops,类似于调用 drm_gpuvm_sm_map()drm_gpuvm_sm_unmap() 时会收到的回调。虽然这种方式需要更多的内存(用于分配 drm_gpuva_ops),但它为驱动程序提供了一种多次迭代 drm_gpuva_op 的方法,例如,一次在可以进行内存分配的上下文中(例如,分配 GPU 页表),一次在 dma-fence 信号关键路径中。

要更新 drm_gpuvm 的 GPU VA 空间视图,可以使用 drm_gpuva_insert()drm_gpuva_remove()。可以从源自 drm_gpuvm_sm_map()drm_gpuvm_sm_unmap()drm_gpuvm_ops 回调中安全地使用这些函数。但是,使用提供的辅助函数 drm_gpuva_map()drm_gpuva_remap()drm_gpuva_unmap() 可能更方便。

下图描绘了现有 GPU VA 映射、新请求的映射和由 drm_gpuvm_sm_map() 实现的生成的映射之间的基本关系 - 它不涵盖这些映射的任何任意组合。

  1. 请求的映射是相同的。替换它,但指示可以保留后备 PTE。

         0     a     1
    old: |-----------| (bo_offset=n)
    
         0     a     1
    req: |-----------| (bo_offset=n)
    
         0     a     1
    new: |-----------| (bo_offset=n)
    
  2. 请求的映射是相同的,除了 BO 偏移量,因此替换映射。

         0     a     1
    old: |-----------| (bo_offset=n)
    
         0     a     1
    req: |-----------| (bo_offset=m)
    
         0     a     1
    new: |-----------| (bo_offset=m)
    
  3. 请求的映射是相同的,除了后备 BO,因此替换映射。

         0     a     1
    old: |-----------| (bo_offset=n)
    
         0     b     1
    req: |-----------| (bo_offset=n)
    
         0     b     1
    new: |-----------| (bo_offset=n)
    
  4. 现有映射是请求映射的左对齐子集,因此替换现有映射。

         0  a  1
    old: |-----|       (bo_offset=n)
    
         0     a     2
    req: |-----------| (bo_offset=n)
    
         0     a     2
    new: |-----------| (bo_offset=n)
    

    注意

    对于具有不同 BO 和/或非连续 BO 偏移量的请求,我们期望看到相同的结果。

  5. 请求映射的范围是现有映射的左对齐子集,但由不同的 BO 支持。因此,映射请求的映射并拆分现有映射,调整其 BO 偏移量。

         0     a     2
    old: |-----------| (bo_offset=n)
    
         0  b  1
    req: |-----|       (bo_offset=n)
    
         0  b  1  a' 2
    new: |-----|-----| (b.bo_offset=n, a.bo_offset=n+1)
    

    注意

    对于具有不同 BO 和/或非连续 BO 偏移量的请求,我们期望看到相同的结果。

  6. 现有映射是请求映射的超集。将其拆分,但指示可以保留后备 PTE。

         0     a     2
    old: |-----------| (bo_offset=n)
    
         0  a  1
    req: |-----|       (bo_offset=n)
    
         0  a  1  a' 2
    new: |-----|-----| (a.bo_offset=n, a'.bo_offset=n+1)
    
  7. 请求映射的范围是现有映射的右对齐子集,但由不同的 BO 支持。因此,映射请求的映射并拆分现有映射,无需调整 BO 偏移量。

         0     a     2
    old: |-----------| (bo_offset=n)
    
               1  b  2
    req:       |-----| (bo_offset=m)
    
         0  a  1  b  2
    new: |-----|-----| (a.bo_offset=n,b.bo_offset=m)
    
  8. 现有映射是请求映射的超集。将其拆分,但指示可以保留后备 PTE。

          0     a     2
    old: |-----------| (bo_offset=n)
    
               1  a  2
    req:       |-----| (bo_offset=n+1)
    
         0  a' 1  a  2
    new: |-----|-----| (a'.bo_offset=n, a.bo_offset=n+1)
    
  9. 现有映射的末尾与由不同 BO 支持的请求映射重叠。因此,映射请求的映射并拆分现有映射,无需调整 BO 偏移量。

         0     a     2
    old: |-----------|       (bo_offset=n)
    
               1     b     3
    req:       |-----------| (bo_offset=m)
    
         0  a  1     b     3
    new: |-----|-----------| (a.bo_offset=n,b.bo_offset=m)
    
  10. 现有映射与请求的映射重叠,两者都具有相同的后备 BO 和连续的偏移量。指示旧映射的后备 PTE 可以保留。

         0     a     2
    old: |-----------|       (bo_offset=n)
    
               1     a     3
    req:       |-----------| (bo_offset=n+1)
    
         0  a' 1     a     3
    new: |-----|-----------| (a'.bo_offset=n, a.bo_offset=n+1)
    
  11. 请求映射的范围是现有映射的中心子集,具有不同的后备 BO。因此,映射请求的映射并将现有映射拆分为两个映射,相应地调整右侧映射的 BO 偏移量。

         0        a        3
    old: |-----------------| (bo_offset=n)
    
               1  b  2
    req:       |-----|       (bo_offset=m)
    
         0  a  1  b  2  a' 3
    new: |-----|-----|-----| (a.bo_offset=n,b.bo_offset=m,a'.bo_offset=n+2)
    
  12. 请求的映射是现有映射的连续子集。将其拆分,但指示可以保留后备 PTE。

         0        a        3
    old: |-----------------| (bo_offset=n)
    
               1  a  2
    req:       |-----|       (bo_offset=n+1)
    
         0  a' 1  a  2 a'' 3
    old: |-----|-----|-----| (a'.bo_offset=n, a.bo_offset=n+1, a''.bo_offset=n+2)
    
  13. 现有映射是请求映射的右对齐子集,因此替换现有映射。

               1  a  2
    old:       |-----| (bo_offset=n+1)
    
         0     a     2
    req: |-----------| (bo_offset=n)
    
         0     a     2
    new: |-----------| (bo_offset=n)
    

    注意

    对于具有不同 BO 和/或非连续 bo_offset 的请求,我们期望看到相同的结果。

  14. 现有映射是请求映射的中心子集,因此替换现有映射。

               1  a  2
    old:       |-----| (bo_offset=n+1)
    
         0        a       3
    req: |----------------| (bo_offset=n)
    
         0        a       3
    new: |----------------| (bo_offset=n)
    

    注意

    对于具有不同 BO 和/或非连续 bo_offset 的请求,我们期望看到相同的结果。

  15. 现有映射的开头与由不同 BO 支持的请求映射重叠。因此,映射请求的映射并拆分现有映射,相应地调整其 BO 偏移量。

               1     a     3
    old:       |-----------| (bo_offset=n)
    
         0     b     2
    req: |-----------|       (bo_offset=m)
    
         0     b     2  a' 3
    new: |-----------|-----| (b.bo_offset=m,a.bo_offset=n+2)
    

锁定

在管理 drm_gpuva 条目方面,DRM GPUVM 本身不负责锁定,驱动程序有责任负责锁定。驱动程序可能需要保护以下操作:插入、删除和迭代 drm_gpuva 对象以及生成各种操作,例如拆分/合并或预取。

DRM GPUVM 本身也不负责锁定后备 drm_gem_object 缓冲区 GPU VA 列表和 drm_gpuvm_bo 抽象;驱动程序有责任使用 GEM 的 dma_resv 锁或驱动程序特定的外部锁来强制执行互斥。对于后者,另请参见 drm_gem_gpuva_set_lock()

但是,DRM GPUVM 包含 lockdep 检查,以确保其 API 的调用者在 drm_gem_objects GPU VA 列表被 drm_gpuva_link()drm_gpuva_unlink() 等函数访问时,持有相应的锁,以及 drm_gpuvm_bo_obtain()drm_gpuvm_bo_put()

后者是必需的,因为在创建和销毁 drm_gpuvm_bo 时,drm_gpuvm_bo 会附加/从 drm_gem_objects 的 gpuva 列表中删除。为了保持实例的唯一性,后续对相同 drm_gpuvmdrm_gem_objectdrm_gpuvm_bo_obtain() 调用必须能够观察到先前 drm_gpuvm_bos 的创建和销毁。

保护 drm_gpuvm 用于跟踪外部和驱逐对象的列表,使其免受并发插入/删除和内部迭代的影响。

但是,驱动程序仍然需要确保保护对迭代这些列表的函数的并发调用,即 drm_gpuvm_prepare_objects()drm_gpuvm_validate()

或者,驱动程序可以设置 DRM_GPUVM_RESV_PROTECTED 标志,以表明相应的 dma_resv 锁被持有,以保护列表。如果设置了 DRM_GPUVM_RESV_PROTECTED,则内部锁定将被禁用,并且相应的锁依赖检查将被启用。对于能够获取相应的 dma_resv 锁的驱动程序来说,这是一种优化,因此它们不需要内部锁定。

示例

本节提供了两个示例,说明如何让 DRM GPUVA 管理器生成 drm_gpuva_op 以满足给定的映射或取消映射请求,以及如何使用它们。

以下代码严格限制于说明通用使用模式。为了保持简洁,它没有使用任何常见代码的抽象、带有栅栏信号的关键路径的不同(异步)阶段、任何其他助手或在释放内存和放弃先前获取的锁方面的错误处理。

  1. 获取 drm_gpuva_op 列表以创建新映射

    // Allocates a new &drm_gpuva.
    struct drm_gpuva * driver_gpuva_alloc(void);
    
    // Typically drivers would embedd the &drm_gpuvm and &drm_gpuva
    // structure in individual driver structures and lock the dma-resv with
    // drm_exec or similar helpers.
    int driver_mapping_create(struct drm_gpuvm *gpuvm,
                              u64 addr, u64 range,
                              struct drm_gem_object *obj, u64 offset)
    {
            struct drm_gpuva_ops *ops;
            struct drm_gpuva_op *op
            struct drm_gpuvm_bo *vm_bo;
    
            driver_lock_va_space();
            ops = drm_gpuvm_sm_map_ops_create(gpuvm, addr, range,
                                              obj, offset);
            if (IS_ERR(ops))
                    return PTR_ERR(ops);
    
            vm_bo = drm_gpuvm_bo_obtain(gpuvm, obj);
            if (IS_ERR(vm_bo))
                    return PTR_ERR(vm_bo);
    
            drm_gpuva_for_each_op(op, ops) {
                    struct drm_gpuva *va;
    
                    switch (op->op) {
                    case DRM_GPUVA_OP_MAP:
                            va = driver_gpuva_alloc();
                            if (!va)
                                    ; // unwind previous VA space updates,
                                      // free memory and unlock
    
                            driver_vm_map();
                            drm_gpuva_map(gpuvm, va, &op->map);
                            drm_gpuva_link(va, vm_bo);
    
                            break;
                    case DRM_GPUVA_OP_REMAP: {
                            struct drm_gpuva *prev = NULL, *next = NULL;
    
                            va = op->remap.unmap->va;
    
                            if (op->remap.prev) {
                                    prev = driver_gpuva_alloc();
                                    if (!prev)
                                            ; // unwind previous VA space
                                              // updates, free memory and
                                              // unlock
                            }
    
                            if (op->remap.next) {
                                    next = driver_gpuva_alloc();
                                    if (!next)
                                            ; // unwind previous VA space
                                              // updates, free memory and
                                              // unlock
                            }
    
                            driver_vm_remap();
                            drm_gpuva_remap(prev, next, &op->remap);
    
                            if (prev)
                                    drm_gpuva_link(prev, va->vm_bo);
                            if (next)
                                    drm_gpuva_link(next, va->vm_bo);
                            drm_gpuva_unlink(va);
    
                            break;
                    }
                    case DRM_GPUVA_OP_UNMAP:
                            va = op->unmap->va;
    
                            driver_vm_unmap();
                            drm_gpuva_unlink(va);
                            drm_gpuva_unmap(&op->unmap);
    
                            break;
                    default:
                            break;
                    }
            }
            drm_gpuvm_bo_put(vm_bo);
            driver_unlock_va_space();
    
            return 0;
    }
    
  2. 接收每个 drm_gpuva_op 的回调以创建新映射

    struct driver_context {
            struct drm_gpuvm *gpuvm;
            struct drm_gpuvm_bo *vm_bo;
            struct drm_gpuva *new_va;
            struct drm_gpuva *prev_va;
            struct drm_gpuva *next_va;
    };
    
    // ops to pass to drm_gpuvm_init()
    static const struct drm_gpuvm_ops driver_gpuvm_ops = {
            .sm_step_map = driver_gpuva_map,
            .sm_step_remap = driver_gpuva_remap,
            .sm_step_unmap = driver_gpuva_unmap,
    };
    
    // Typically drivers would embedd the &drm_gpuvm and &drm_gpuva
    // structure in individual driver structures and lock the dma-resv with
    // drm_exec or similar helpers.
    int driver_mapping_create(struct drm_gpuvm *gpuvm,
                              u64 addr, u64 range,
                              struct drm_gem_object *obj, u64 offset)
    {
            struct driver_context ctx;
            struct drm_gpuvm_bo *vm_bo;
            struct drm_gpuva_ops *ops;
            struct drm_gpuva_op *op;
            int ret = 0;
    
            ctx.gpuvm = gpuvm;
    
            ctx.new_va = kzalloc(sizeof(*ctx.new_va), GFP_KERNEL);
            ctx.prev_va = kzalloc(sizeof(*ctx.prev_va), GFP_KERNEL);
            ctx.next_va = kzalloc(sizeof(*ctx.next_va), GFP_KERNEL);
            ctx.vm_bo = drm_gpuvm_bo_create(gpuvm, obj);
            if (!ctx.new_va || !ctx.prev_va || !ctx.next_va || !vm_bo) {
                    ret = -ENOMEM;
                    goto out;
            }
    
            // Typically protected with a driver specific GEM gpuva lock
            // used in the fence signaling path for drm_gpuva_link() and
            // drm_gpuva_unlink(), hence pre-allocate.
            ctx.vm_bo = drm_gpuvm_bo_obtain_prealloc(ctx.vm_bo);
    
            driver_lock_va_space();
            ret = drm_gpuvm_sm_map(gpuvm, &ctx, addr, range, obj, offset);
            driver_unlock_va_space();
    
    out:
            drm_gpuvm_bo_put(ctx.vm_bo);
            kfree(ctx.new_va);
            kfree(ctx.prev_va);
            kfree(ctx.next_va);
            return ret;
    }
    
    int driver_gpuva_map(struct drm_gpuva_op *op, void *__ctx)
    {
            struct driver_context *ctx = __ctx;
    
            drm_gpuva_map(ctx->vm, ctx->new_va, &op->map);
    
            drm_gpuva_link(ctx->new_va, ctx->vm_bo);
    
            // prevent the new GPUVA from being freed in
            // driver_mapping_create()
            ctx->new_va = NULL;
    
            return 0;
    }
    
    int driver_gpuva_remap(struct drm_gpuva_op *op, void *__ctx)
    {
            struct driver_context *ctx = __ctx;
            struct drm_gpuva *va = op->remap.unmap->va;
    
            drm_gpuva_remap(ctx->prev_va, ctx->next_va, &op->remap);
    
            if (op->remap.prev) {
                    drm_gpuva_link(ctx->prev_va, va->vm_bo);
                    ctx->prev_va = NULL;
            }
    
            if (op->remap.next) {
                    drm_gpuva_link(ctx->next_va, va->vm_bo);
                    ctx->next_va = NULL;
            }
    
            drm_gpuva_unlink(va);
            kfree(va);
    
            return 0;
    }
    
    int driver_gpuva_unmap(struct drm_gpuva_op *op, void *__ctx)
    {
            drm_gpuva_unlink(op->unmap.va);
            drm_gpuva_unmap(&op->unmap);
            kfree(op->unmap.va);
    
            return 0;
    }
    

DRM GPUVM 函数参考

enum drm_gpuva_flags

struct drm_gpuva 的标志

常量

DRM_GPUVA_INVALIDATED

标志指示 drm_gpuva 的后备 GEM 已失效。

DRM_GPUVA_SPARSE

标志指示 drm_gpuva 是稀疏映射。

DRM_GPUVA_USERBITS

用户定义的位

struct drm_gpuva

跟踪 GPU VA 映射的结构

定义:

struct drm_gpuva {
    struct drm_gpuvm *vm;
    struct drm_gpuvm_bo *vm_bo;
    enum drm_gpuva_flags flags;
    struct {
        u64 addr;
        u64 range;
    } va;
    struct {
        u64 offset;
        struct drm_gem_object *obj;
        struct list_head entry;
    } gem;
    struct {
        struct rb_node node;
        struct list_head entry;
        u64 __subtree_last;
    } rb;
};

成员

vm

此对象关联的 drm_gpuvm

vm_bo

映射的 drm_gem_objectdrm_gpuvm_bo 抽象

flags

此映射的 drm_gpuva_flags

va

包含 drm_gpuva 的地址和范围的结构

va.addr

起始地址

gem

包含 drm_gem_object 及其偏移量的结构

gem.offset

drm_gem_object 中的偏移量

gem.obj

映射的 drm_gem_object

gem.entry

将此对象附加到 drm_gpuvm_bolist_head

rb

包含在 rb-tree 中存储 drm_gpuvas 的数据的结构

rb.node

rb-tree 节点

rb.entry

drm_gpuvas 按它们在区间树中出现的相同顺序额外连接的 list_head。这对于在对 rb-tree 本身进行修改时,保持从通过 rb-tree 找到的起始节点迭代 drm_gpuvas 非常有用。

rb.__subtree_last

区间树需要,持有子树中的最后一个

描述

此结构表示 GPU VA 映射,并与 drm_gpuvm 关联。

通常,此结构嵌入在更大的驱动程序结构中。

void drm_gpuva_invalidate(struct drm_gpuva *va, bool invalidate)

设置此 drm_gpuva 的后备 GEM 是否失效

参数

struct drm_gpuva *va

要设置失效标志的 drm_gpuva

bool invalidate

指示 drm_gpuva 是否失效

bool drm_gpuva_invalidated(struct drm_gpuva *va)

指示此 drm_gpuva 的后备 BO 是否失效

参数

struct drm_gpuva *va

要检查的 drm_gpuva

返回

如果 GPU VA 已失效,则为 true,否则为 false

enum drm_gpuvm_flags

struct drm_gpuvm 的标志

常量

DRM_GPUVM_RESV_PROTECTED

GPUVM 由 GPUVM 的 dma_resv 锁在外部保护

DRM_GPUVM_USERBITS

用户定义的位

struct drm_gpuvm

DRM GPU VA 管理器

定义:

struct drm_gpuvm {
    const char *name;
    enum drm_gpuvm_flags flags;
    struct drm_device *drm;
    u64 mm_start;
    u64 mm_range;
    struct {
        struct rb_root_cached tree;
        struct list_head list;
    } rb;
    struct kref kref;
    struct drm_gpuva kernel_alloc_node;
    const struct drm_gpuvm_ops *ops;
    struct drm_gem_object *r_obj;
    struct {
        struct list_head list;
        struct list_head *local_list;
        spinlock_t lock;
    } extobj;
    struct {
        struct list_head list;
        struct list_head *local_list;
        spinlock_t lock;
    } evict;
};

成员

name

DRM GPU VA 空间的名称

flags

此 GPUVM 的 drm_gpuvm_flags

drm

此 VM 所在的 drm_device

mm_start

VA 空间的起始

mm_range

VA 空间的长度

rb

用于跟踪 drm_gpuva 条目的结构

rb.tree

用于跟踪 GPU VA 映射的 rb-tree

rb.list

用于跟踪 GPU VA 映射的 list_head

kref

此对象的引用计数

kernel_alloc_node

表示为内核保留的地址空间切口的 drm_gpuva

ops

为驱动程序提供拆分/合并步骤的 drm_gpuvm_ops

r_obj

Resv GEM 对象;表示 GPUVM 的公共 dma_resv

extobj

包含 extobj 列表的结构

extobj.list

存储作为外部对象的 drm_gpuvm_boslist_head

extobj.local_list

指向临时存储来自外部对象列表的条目的本地列表的指针

extobj.lock

用于保护 extobj 列表的自旋锁

evict

包含驱逐列表和驱逐列表锁的结构

evict.list

存储当前正在驱逐的 drm_gpuvm_boslist_head

evict.local_list

指向临时存储来自驱逐对象列表的条目的本地列表的指针

evict.lock

用于保护驱逐列表的自旋锁

描述

DRM GPU VA 管理器通过使用 maple_tree 结构来跟踪 GPU 的虚拟地址空间。通常,此结构嵌入在更大的驱动程序结构中。

驱动程序可以以任意单位(例如,字节或页)传递地址和范围。

每个 GPU 虚拟地址空间应该有一个管理器实例。

struct drm_gpuvm *drm_gpuvm_get(struct drm_gpuvm *gpuvm)

获取 struct drm_gpuvm 的引用

参数

struct drm_gpuvm *gpuvm

要获取引用的 drm_gpuvm

描述

此函数获取对 gpuvm 的额外引用。在没有持有引用时调用此函数是非法的。不需要锁。

返回

struct drm_gpuvm 指针

bool drm_gpuvm_resv_protected(struct drm_gpuvm *gpuvm)

指示是否设置了 DRM_GPUVM_RESV_PROTECTED

参数

struct drm_gpuvm *gpuvm

drm_gpuvm

返回

如果设置了 DRM_GPUVM_RESV_PROTECTED,则为 true,否则为 false。

drm_gpuvm_resv

drm_gpuvm_resv (gpuvm__)

返回 drm_gpuvmdma_resv

参数

gpuvm__

drm_gpuvm

返回

指向 drm_gpuvm 的共享 dma_resv 的指针

drm_gpuvm_resv_obj

drm_gpuvm_resv_obj (gpuvm__)

返回持有 drm_gpuvmdma_resvdrm_gem_object

参数

gpuvm__

drm_gpuvm

返回

指向持有 drm_gpuvm 的共享 dma_resvdrm_gem_object 的指针

bool drm_gpuvm_is_extobj(struct drm_gpuvm *gpuvm, struct drm_gem_object *obj)

指示给定的 drm_gem_object 是否是外部对象

参数

struct drm_gpuvm *gpuvm

要检查的 drm_gpuvm

struct drm_gem_object *obj

要检查的 drm_gem_object

返回

如果 drm_gem_objectdma_resvdrm_gpuvmsdma_resv 不同,则为 true,否则为 false

drm_gpuvm_for_each_va_range

drm_gpuvm_for_each_va_range (va__, gpuvm__, start__, end__)

迭代 drm_gpuvas 的范围

参数

va__

在每个迭代步骤中要赋值的 drm_gpuva 结构

gpuvm__

要遍历的 drm_gpuvm

start__

起始偏移量,第一个 gpuva 将与此重叠

end__

结束偏移量,最后一个 gpuva 将在此之前开始(但可能会重叠)

描述

此迭代器遍历 start__end__ 之间的 drm_gpuvm 中的所有 drm_gpuvas。它的实现类似于 list_for_each(),但使用 drm_gpuvm 的内部区间树来加速搜索起始 drm_gpuva,因此对于元素的删除是不安全的。它假设 end__drm_gpuvm 内(或为上限)。此迭代器不会跳过 drm_gpuvmkernel_alloc_node

drm_gpuvm_for_each_va_range_safe

drm_gpuvm_for_each_va_range_safe (va__, next__, gpuvm__, start__, end__)

安全地迭代 drm_gpuvas 的范围

参数

va__

在每个迭代步骤中要赋值的 drm_gpuva

next__

要用作临时存储的另一个 drm_gpuva

gpuvm__

要遍历的 drm_gpuvm

start__

起始偏移量,第一个 gpuva 将与此重叠

end__

结束偏移量,最后一个 gpuva 将在此之前开始(但可能会重叠)

描述

此迭代器遍历 start__end__ 之间的 drm_gpuvm 中的所有 drm_gpuvas。它的实现类似于 list_for_each_safe(),但使用 drm_gpuvm 的内部区间树来加速搜索起始 drm_gpuva,因此对于元素的删除是安全的。它假设 end__drm_gpuvm 内(或为上限)。此迭代器不会跳过 drm_gpuvmkernel_alloc_node

drm_gpuvm_for_each_va

drm_gpuvm_for_each_va (va__, gpuvm__)

迭代所有 drm_gpuvas

参数

va__

在每个迭代步骤中要赋值的 drm_gpuva

gpuvm__

要遍历的 drm_gpuvm

描述

此迭代器遍历与给定 drm_gpuvm 关联的所有 drm_gpuva 结构。

drm_gpuvm_for_each_va_safe

drm_gpuvm_for_each_va_safe (va__, next__, gpuvm__)

安全地迭代所有 drm_gpuvas

参数

va__

在每个迭代步骤中要赋值的 drm_gpuva

next__

要用作临时存储的另一个 drm_gpuva

gpuvm__

要遍历的 drm_gpuvm

描述

此迭代器遍历与给定 drm_gpuvm 关联的所有 drm_gpuva 结构。它是使用 list_for_each_entry_safe() 实现的,因此对于元素的删除是安全的。

struct drm_gpuvm_exec

drm_gpuvmdrm_exec 抽象

定义:

struct drm_gpuvm_exec {
    struct drm_exec exec;
    u32 flags;
    struct drm_gpuvm *vm;
    unsigned int num_fences;
    struct {
        int (*fn)(struct drm_gpuvm_exec *vm_exec);
        void *priv;
    } extra;
};

成员

exec

drm_exec 结构

flags

struct drm_exec 的标志

vm

要锁定其 DMA 预留的 drm_gpuvm

num_fences

为锁定的 drm_gem_objectsdma_resv 保留的栅栏数量

额外

驱动程序锁定任意其他 drm_gem_objects 的回调函数和相应的私有数据。

extra.fn

驱动程序回调函数,用于锁定额外的 drm_gem_objects

extra.priv

用于 fn 回调函数的驱动程序私有数据

描述

此结构应在堆栈上创建,如同 drm_exec 一样。

可以选择设置 extra 以便锁定额外的 drm_gem_objects

void drm_gpuvm_exec_unlock(struct drm_gpuvm_exec *vm_exec)

锁定所有关联的 BO 的所有 dma-resv

参数

struct drm_gpuvm_exec *vm_exec

drm_gpuvm_exec 包装器

描述

释放先前通过 drm_gpuvm_exec_lock() 或其变体获取的所有 drm_gem_objects 的所有 dma-resv 锁。

返回

成功时为 0,失败时为负错误代码。

void drm_gpuvm_exec_resv_add_fence(struct drm_gpuvm_exec *vm_exec, struct dma_fence *fence, enum dma_resv_usage private_usage, enum dma_resv_usage extobj_usage)

将栅栏添加到私有和所有 extobj

参数

struct drm_gpuvm_exec *vm_exec

drm_gpuvm_exec 包装器

struct dma_fence *fence

要添加的栅栏

enum dma_resv_usage private_usage

私有 dma-resv 用法

enum dma_resv_usage extobj_usage

extobj dma-resv 用法

描述

请参阅 drm_gpuvm_resv_add_fence()

int drm_gpuvm_exec_validate(struct drm_gpuvm_exec *vm_exec)

验证所有标记为已逐出的 BO

参数

struct drm_gpuvm_exec *vm_exec

drm_gpuvm_exec 包装器

描述

请参阅 drm_gpuvm_validate()

返回

成功时为 0,失败时为负错误代码。

struct drm_gpuvm_bo

表示 drm_gpuvmdrm_gem_object 组合的结构

定义:

struct drm_gpuvm_bo {
    struct drm_gpuvm *vm;
    struct drm_gem_object *obj;
    bool evicted;
    struct kref kref;
    struct {
        struct list_head gpuva;
        struct {
            struct list_head gem;
            struct list_head extobj;
            struct list_head evict;
        } entry;
    } list;
};

成员

vm

obj 映射到的 drm_gpuvm。这是一个引用计数指针。

obj

正在 vm 中映射的 drm_gem_object。这是一个引用计数指针。

已逐出

指示 drm_gem_object 是否已逐出;该字段受 drm_gem_object 的 dma-resv 锁保护。

kref

drm_gpuvm_bo 的引用计数。

list

包含所有 list_heads 的结构。

list.gpuva

链接的 drm_gpuvas 的列表。

只要持有 GEM 的 gpuva 锁,就可以安全地访问此列表中的条目。另请参阅 struct drm_gem_object

list.entry

包含所有用作条目的 list_heads 的结构。

list.entry.gem

要附加到 drm_gem_objects gpuva 列表的列表条目。

list.entry.evict

要附加到 drm_gpuvms 逐出列表的列表条目。

描述

此结构是一个抽象,表示 drm_gpuvmdrm_gem_object 的组合。它用作间接方式,以加速迭代由同一 drm_gem_object 支持的 drm_gpuvm 中的所有 drm_gpuvas

此外,它用于缓存特定 GPU-VM 的已逐出 GEM 对象,以加速验证。

通常,驱动程序希望在 GEM 对象首次映射到 GPU-VM 时创建 struct drm_gpuvm_bo 的实例,并在此 GPU-VM 中 GEM 对象的最后一次映射取消映射后释放该实例。

struct drm_gpuvm_bo *drm_gpuvm_bo_get(struct drm_gpuvm_bo *vm_bo)

获取 struct drm_gpuvm_bo 引用

参数

struct drm_gpuvm_bo *vm_bo

要获取其引用的 drm_gpuvm_bo

描述

此函数获取对 vm_bo 的额外引用。在没有持有引用的情况下调用此函数是非法的。不需要锁。

返回

struct vm_bo 指针

void drm_gpuvm_bo_gem_evict(struct drm_gem_object *obj, bool evict)

将列表中的所有 drm_gpuvm_bo 添加/删除到/从 drm_gpuvms 逐出列表中

参数

struct drm_gem_object *obj

the drm_gem_object

bool evict

指示 obj 是否已逐出

描述

请参阅 drm_gpuvm_bo_evict()

drm_gpuvm_bo_for_each_va

drm_gpuvm_bo_for_each_va (va__, vm_bo__)

迭代器,用于遍历 drm_gpuva 列表

参数

va__

在每个迭代步骤中要赋值的 drm_gpuva 结构

vm_bo__

drm_gpuvm_bo,要遍历的 drm_gpuva 与之关联

描述

此迭代器遍历与 drm_gpuvm_bo 关联的所有 drm_gpuva 结构。

调用者必须持有 GEM 的 gpuva 锁。

drm_gpuvm_bo_for_each_va_safe

drm_gpuvm_bo_for_each_va_safe (va__, next__, vm_bo__)

安全遍历 drm_gpuva 列表的迭代器

参数

va__

在每个迭代步骤中要赋值的 drm_gpuva 结构

next__

next drm_gpuva 用于存储下一步

vm_bo__

drm_gpuvm_bo,要遍历的 drm_gpuva 与之关联

描述

此迭代器遍历与 drm_gpuvm_bo 关联的所有 drm_gpuva 结构。它使用 list_for_each_entry_safe() 实现,因此可以安全地删除元素。

调用者必须持有 GEM 的 gpuva 锁。

enum drm_gpuva_op_type

GPU VA 操作类型

常量

DRM_GPUVA_OP_MAP

映射操作类型

DRM_GPUVA_OP_REMAP

重映射操作类型

DRM_GPUVA_OP_UNMAP

取消映射操作类型

DRM_GPUVA_OP_PREFETCH

预取操作类型

描述

用于更改由 drm_gpuvm 跟踪的 GPU VA 映射的操作。

struct drm_gpuva_op_map

GPU VA 映射操作

定义:

struct drm_gpuva_op_map {
    struct {
        u64 addr;
        u64 range;
    } va;
    struct {
        u64 offset;
        struct drm_gem_object *obj;
    } gem;
};

成员

va

包含映射操作的地址和范围的结构

va.addr

新映射的基址

va.range

新映射的范围

gem

包含 drm_gem_object 及其偏移量的结构

gem.offset

drm_gem_object 中的偏移量

gem.obj

要映射的 drm_gem_object

描述

此结构表示由 DRM GPU VA 管理器生成的单个映射操作。

struct drm_gpuva_op_unmap

GPU VA 取消映射操作

定义:

struct drm_gpuva_op_unmap {
    struct drm_gpuva *va;
    bool keep;
};

成员

va

要取消映射的 drm_gpuva

keep

指示此 drm_gpuva 在物理上是否与原始映射请求连续。

可选地,如果设置了 keep,驱动程序可以保留此 drm_gpuva 的实际页表映射,仅添加缺少的页表条目并相应地更新 drm_gpuvm

描述

此结构表示由 DRM GPU VA 管理器生成的单个取消映射操作。

struct drm_gpuva_op_remap

GPU VA 重映射操作

定义:

struct drm_gpuva_op_remap {
    struct drm_gpuva_op_map *prev;
    struct drm_gpuva_op_map *next;
    struct drm_gpuva_op_unmap *unmap;
};

成员

prev

分割映射的前一部分

next

分割映射的后一部分

unmap

原始现有映射的取消映射操作

描述

这表示由 DRM GPU VA 管理器生成的单个重映射操作。

当通过插入新的 GPU VA 映射或通过部分取消映射现有映射来分割现有 GPU VA 映射时,会生成重映射操作,因此它最多包含两个映射操作和一个取消映射操作。

unmap 操作负责删除原始现有映射。 prev 用于重映射前一部分,next 用于重映射后一部分。

如果新映射的起始地址与旧映射的起始地址对齐,或者新映射的结束地址与旧映射的结束地址对齐,则 prevnext 为 NULL。

请注意,使用专用重映射操作而不是任意取消映射和映射操作的原因是,使驱动程序有机会从取消映射操作的 drm_gpuva 结构(通常嵌入在更大的驱动程序特定结构中)中提取用于创建新映射的驱动程序特定数据。

struct drm_gpuva_op_prefetch

GPU VA 预取操作

定义:

struct drm_gpuva_op_prefetch {
    struct drm_gpuva *va;
};

成员

va

要预取的 drm_gpuva

描述

此结构表示由 DRM GPU VA 管理器生成的单个预取操作。

struct drm_gpuva_op

GPU VA 操作

定义:

struct drm_gpuva_op {
    struct list_head entry;
    enum drm_gpuva_op_type op;
    union {
        struct drm_gpuva_op_map map;
        struct drm_gpuva_op_remap remap;
        struct drm_gpuva_op_unmap unmap;
        struct drm_gpuva_op_prefetch prefetch;
    };
};

成员

entry

用于在 drm_gpuva_ops 中分发此结构实例的 list_head

op

操作的类型

{unnamed_union}

匿名

map

映射操作

remap

重映射操作

unmap

取消映射操作

prefetch

预取操作

描述

此结构表示单个通用操作。

操作的特定类型由 op 定义。

struct drm_gpuva_ops

包装 drm_gpuva_op 的列表

定义:

struct drm_gpuva_ops {
    struct list_head list;
};

成员

list

list_head

drm_gpuva_for_each_op

drm_gpuva_for_each_op (op, ops)

用于遍历 drm_gpuva_ops 的迭代器

参数

op

在每个迭代步骤中要分配的 drm_gpuva_op

ops

要遍历的 drm_gpuva_ops

描述

此迭代器遍历给定操作列表中的所有操作。

drm_gpuva_for_each_op_safe

drm_gpuva_for_each_op_safe (op, next, ops)

用于安全遍历 drm_gpuva_ops 的迭代器

参数

op

在每个迭代步骤中要分配的 drm_gpuva_op

next

next drm_gpuva_op 用于存储下一步

ops

要遍历的 drm_gpuva_ops

描述

此迭代器遍历给定操作列表中的所有操作。它使用 list_for_each_safe() 实现,因此可以安全地删除元素。

drm_gpuva_for_each_op_from_reverse

drm_gpuva_for_each_op_from_reverse (op, ops)

从给定点向后迭代

参数

op

在每个迭代步骤中要分配的 drm_gpuva_op

ops

要遍历的 drm_gpuva_ops

描述

此迭代器从给定操作开始,以相反的顺序遍历给定操作列表中的所有操作。

drm_gpuva_for_each_op_reverse

drm_gpuva_for_each_op_reverse (op, ops)

用于以相反顺序遍历 drm_gpuva_ops 的迭代器

参数

op

在每个迭代步骤中要分配的 drm_gpuva_op

ops

要遍历的 drm_gpuva_ops

描述

此迭代器以相反顺序遍历给定操作列表中的所有操作

drm_gpuva_first_op

drm_gpuva_first_op (ops)

drm_gpuva_ops 返回第一个 drm_gpuva_op

参数

ops

要从中获取第一个 drm_gpuva_opdrm_gpuva_ops

drm_gpuva_last_op

drm_gpuva_last_op (ops)

drm_gpuva_ops 返回最后一个 drm_gpuva_op

参数

ops

要从中获取最后一个 drm_gpuva_opdrm_gpuva_ops

drm_gpuva_prev_op

drm_gpuva_prev_op (op)

列表中上一个 drm_gpuva_op

参数

op

当前 drm_gpuva_op

drm_gpuva_next_op

drm_gpuva_next_op (op)

列表中下一个 drm_gpuva_op

参数

op

当前 drm_gpuva_op

struct drm_gpuvm_ops

用于拆分/合并步骤的回调

定义:

struct drm_gpuvm_ops {
    void (*vm_free)(struct drm_gpuvm *gpuvm);
    struct drm_gpuva_op *(*op_alloc)(void);
    void (*op_free)(struct drm_gpuva_op *op);
    struct drm_gpuvm_bo *(*vm_bo_alloc)(void);
    void (*vm_bo_free)(struct drm_gpuvm_bo *vm_bo);
    int (*vm_bo_validate)(struct drm_gpuvm_bo *vm_bo, struct drm_exec *exec);
    int (*sm_step_map)(struct drm_gpuva_op *op, void *priv);
    int (*sm_step_remap)(struct drm_gpuva_op *op, void *priv);
    int (*sm_step_unmap)(struct drm_gpuva_op *op, void *priv);
};

成员

vm_free

struct drm_gpuvm 的最后一个引用被删除时调用

此回调是必需的。

op_alloc

drm_gpuvm 分配 struct drm_gpuva_op 时调用

某些驱动程序可能希望将 struct drm_gpuva_op 嵌入到特定于驱动程序的结构中。通过实现此回调,驱动程序可以相应地分配内存。

此回调是可选的。

op_free

drm_gpuvm 释放 struct drm_gpuva_op 时调用

某些驱动程序可能希望将 struct drm_gpuva_op 嵌入到特定于驱动程序的结构中。通过实现此回调,驱动程序可以相应地释放先前分配的内存。

此回调是可选的。

vm_bo_alloc

drm_gpuvm 分配 struct drm_gpuvm_bo 时调用

某些驱动程序可能希望将 struct drm_gpuvm_bo 嵌入到特定于驱动程序的结构中。通过实现此回调,驱动程序可以相应地分配内存。

此回调是可选的。

vm_bo_free

drm_gpuvm 释放 struct drm_gpuvm_bo 时调用

某些驱动程序可能希望将 struct drm_gpuvm_bo 嵌入到特定于驱动程序的结构中。通过实现此回调,驱动程序可以相应地释放先前分配的内存。

此回调是可选的。

vm_bo_validate

drm_gpuvm_validate() 调用

对于在相应的 drm_gpuvm 中映射的每个被驱逐的 drm_gem_object,驱动程序都会收到此回调。

通常,驱动程序会在此回调中调用其特定于驱动程序的 ttm_bo_validate() 变体。

sm_step_map

drm_gpuvm_sm_map 调用,以便在完成所有先前的步骤后最终插入映射

priv 指针与驱动程序传递给 drm_gpuvm_sm_mapdrm_gpuvm_sm_unmap 的指针匹配。

如果使用 drm_gpuvm_sm_map,则可以为 NULL。

sm_step_remap

drm_gpuvm_sm_mapdrm_gpuvm_sm_unmap 调用,以拆分现有的映射

当需要拆分现有映射时,会调用此回调。当新请求的映射与现有映射重叠或被现有映射包围,或者请求部分取消映射现有映射时,就会发生这种情况。

priv 指针与驱动程序传递给 drm_gpuvm_sm_mapdrm_gpuvm_sm_unmap 的指针匹配。

如果既不使用 drm_gpuvm_sm_map 也不使用 drm_gpuvm_sm_unmap,则可以为 NULL。

sm_step_unmap

drm_gpuvm_sm_mapdrm_gpuvm_sm_unmap 调用,以取消映射现有的映射

当需要取消映射现有映射时,会调用此回调。当新请求的映射包围现有映射或请求取消映射现有映射时,就会发生这种情况。

priv 指针与驱动程序传递给 drm_gpuvm_sm_mapdrm_gpuvm_sm_unmap 的指针匹配。

如果既不使用 drm_gpuvm_sm_map 也不使用 drm_gpuvm_sm_unmap,则可以为 NULL。

描述

此结构定义了 drm_gpuvm_sm_mapdrm_gpuvm_sm_unmap 使用的回调,以便为驱动程序提供映射和取消映射操作的拆分/合并步骤。

void drm_gpuva_op_remap_to_unmap_range(const struct drm_gpuva_op_remap *op, u64 *start_addr, u64 *range)

辅助函数,用于获取重映射操作的取消映射阶段的起始地址和范围。

参数

const struct drm_gpuva_op_remap *op

重映射操作。

u64 *start_addr

所需取消映射起始地址的输出指针。

u64 *range

所需取消映射长度的输出指针。

描述

将设置给定的起始地址和范围,使其表示先前被重新映射的映射覆盖的地址空间范围,但现在为空。

bool drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, u64 addr, u64 range)

检查给定的范围对于给定的 drm_gpuvm 是否有效

参数

struct drm_gpuvm *gpuvm

用于检查范围的 GPUVM

u64 addr

基地址

u64 range

从基地址开始的范围

描述

检查该范围是否在 GPUVM 的管理边界内。

返回

有效范围返回 true,否则返回 false

struct drm_gem_object *drm_gpuvm_resv_object_alloc(struct drm_device *drm)

分配一个虚拟 drm_gem_object

参数

struct drm_device *drm

驱动程序的 drm_device

描述

分配一个虚拟 drm_gem_object,它可以传递给 drm_gpuvm_init(),以便用作根 GEM 对象,从而提供跨单个 GPUVM 本地的 drm_gem_objects 共享的 drm_resv

返回

成功时返回 drm_gem_object,失败时返回 NULL

void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, enum drm_gpuvm_flags flags, struct drm_device *drm, struct drm_gem_object *r_obj, u64 start_offset, u64 range, u64 reserve_offset, u64 reserve_range, const struct drm_gpuvm_ops *ops)

初始化一个 drm_gpuvm

参数

struct drm_gpuvm *gpuvm

指向要初始化的 drm_gpuvm 的指针

const char *name

GPU VA 空间的名称

enum drm_gpuvm_flags flags

此 GPUVM 的 drm_gpuvm_flags

struct drm_device *drm

此 VM 所在的 drm_device

struct drm_gem_object *r_obj

提供 GPUVM 通用 dma_resv 的 resv drm_gem_object

u64 start_offset

GPU VA 空间的起始偏移量

u64 range

GPU VA 空间的大小

u64 reserve_offset

内核保留的 GPU VA 区域的起始位置

u64 reserve_range

内核保留的 GPU VA 区域的大小

const struct drm_gpuvm_ops *ops

drm_gpuvm_sm_map / drm_gpuvm_sm_unmap 上调用的 drm_gpuvm_ops

描述

必须在使用前使用此函数初始化 drm_gpuvm

请注意,在调用此函数之前,必须将 gpuvm 清零为 0。给定的 name 预计由周围的驱动程序结构管理。

void drm_gpuvm_put(struct drm_gpuvm *gpuvm)

删除 struct drm_gpuvm 引用

参数

struct drm_gpuvm *gpuvm

要释放引用的 drm_gpuvm

描述

这将释放对 gpuvm 的引用。

此函数可以从原子上下文中调用。

int drm_gpuvm_prepare_vm(struct drm_gpuvm *gpuvm, struct drm_exec *exec, unsigned int num_fences)

准备 GPUVM 的通用 dma-resv

参数

struct drm_gpuvm *gpuvm

drm_gpuvm

struct drm_exec *exec

drm_exec 上下文

unsigned int num_fences

要保留的 dma_fences 的数量

描述

为 GPUVM 的虚拟 drm_gem_object 调用 drm_exec_prepare_obj();如果 num_fences 为零,则改为调用 drm_exec_lock_obj()

直接使用此函数时,驱动程序有责任相应地调用 drm_exec_init()drm_exec_fini()

返回

成功时为 0,失败时为负错误代码。

int drm_gpuvm_prepare_objects(struct drm_gpuvm *gpuvm, struct drm_exec *exec, unsigned int num_fences)

准备所有关联的 BO

参数

struct drm_gpuvm *gpuvm

drm_gpuvm

struct drm_exec *exec

drm_exec 锁定上下文

unsigned int num_fences

要保留的 dma_fences 的数量

描述

为给定的 drm_gpuvm 包含的映射中的所有 drm_gem_objects 调用 drm_exec_prepare_obj();如果 num_fences 为零,则改为调用 drm_exec_lock_obj()

直接使用此函数时,驱动程序有责任相应地调用 drm_exec_init()drm_exec_fini()

驱动程序需要确保通过外部 VM 锁或在 drm_exec_until_all_locked() 循环中调用此函数之前调用 drm_gpuvm_prepare_vm() 来保护这种情况,以便 GPUVM 的 dma-resv 锁确保互斥。

注意

此函数可以防止并发插入和删除外部对象,但本身不能防止并发使用。

返回

成功时为 0,失败时为负错误代码。

int drm_gpuvm_prepare_range(struct drm_gpuvm *gpuvm, struct drm_exec *exec, u64 addr, u64 range, unsigned int num_fences)

准备给定范围内的所有映射 BO

参数

struct drm_gpuvm *gpuvm

drm_gpuvm

struct drm_exec *exec

drm_exec 锁定上下文

u64 addr

VA 空间内的起始地址

u64 range

要在 VA 空间中迭代的范围

unsigned int num_fences

要保留的 dma_fences 的数量

描述

为映射在 addraddr + range 之间的所有 drm_gem_objects 调用 drm_exec_prepare_obj();如果 num_fences 为零,则改为调用 drm_exec_lock_obj()

返回

成功时为 0,失败时为负错误代码。

int drm_gpuvm_exec_lock(struct drm_gpuvm_exec *vm_exec)

锁定所有关联的 BO 的所有 dma-resv

参数

struct drm_gpuvm_exec *vm_exec

drm_gpuvm_exec 包装器

描述

获取给定 drm_gpuvm 包含的映射中所有 drm_gem_objects 的所有 dma-resv 锁。

此外,当使用设置了 struct drm_gpuvm_exec::extra 的参数调用此函数时,驱动程序会接收到给定的 fn 回调,以便在 drm_gpuvm_exec 实例的上下文中锁定额外的 dma-resv。通常,驱动程序会在此回调中调用 drm_exec_prepare_obj()

返回

成功时为 0,失败时为负错误代码。

int drm_gpuvm_exec_lock_array(struct drm_gpuvm_exec *vm_exec, struct drm_gem_object **objs, unsigned int num_objs)

锁定所有关联的 BO 的所有 dma-resv

参数

struct drm_gpuvm_exec *vm_exec

drm_gpuvm_exec 包装器

struct drm_gem_object **objs

要锁定的其他 drm_gem_objects

unsigned int num_objs

要锁定的其他 drm_gem_objects 的数量

描述

获取给定 drm_gpuvm 包含映射的所有 drm_gem_objects 的所有 dma-resv 锁,以及通过 objs 给出的锁。

返回

成功时为 0,失败时为负错误代码。

int drm_gpuvm_exec_lock_range(struct drm_gpuvm_exec *vm_exec, u64 addr, u64 range)

准备给定范围内的所有映射 BO

参数

struct drm_gpuvm_exec *vm_exec

drm_gpuvm_exec 包装器

u64 addr

VA 空间内的起始地址

u64 range

要在 VA 空间中迭代的范围

描述

获取映射在 addraddr + range 之间的所有 drm_gem_objects 的所有 dma-resv 锁。

返回

成功时为 0,失败时为负错误代码。

int drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec)

验证所有标记为已逐出的 BO

参数

struct drm_gpuvm *gpuvm

要验证逐出 BO 的 drm_gpuvm

struct drm_exec *exec

用于锁定 GPUVM 的 drm_exec 实例

描述

为给定 drm_gpuvm 中映射的所有逐出的缓冲区对象调用 drm_gpuvm_ops::vm_bo_validate 回调。

返回

成功时为 0,失败时为负错误代码。

void drm_gpuvm_resv_add_fence(struct drm_gpuvm *gpuvm, struct drm_exec *exec, struct dma_fence *fence, enum dma_resv_usage private_usage, enum dma_resv_usage extobj_usage)

将 fence 添加到私有和所有 extobj dma-resv

参数

struct drm_gpuvm *gpuvm

要添加 fence 的 drm_gpuvm

struct drm_exec *exec

drm_exec 锁定上下文

struct dma_fence *fence

要添加的栅栏

enum dma_resv_usage private_usage

私有 dma-resv 用法

enum dma_resv_usage extobj_usage

extobj dma-resv 用法

struct drm_gpuvm_bo *drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm, struct drm_gem_object *obj)

创建 struct drm_gpuvm_bo 的新实例

参数

struct drm_gpuvm *gpuvm

obj 在其中映射的 drm_gpuvm

struct drm_gem_object *obj

gpuvm 中映射的 drm_gem_object

描述

如果驱动程序提供了此函数,则此函数使用 drm_gpuvm_ops vm_bo_alloc() 回调进行分配。

返回

成功时指向 drm_gpuvm_bo 的指针,失败时为 NULL

bool drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo)

删除 struct drm_gpuvm_bo 引用

参数

struct drm_gpuvm_bo *vm_bo

要释放其引用的 drm_gpuvm_bo

描述

这将释放对 vm_bo 的引用。

如果引用计数降至零,则销毁 gpuvm_bo,其中包括将其从 GEM 的 gpuva 列表中删除。因此,如果对此函数的调用可能会使引用计数降至零,则调用方必须持有 dma-resv 或特定于驱动程序的 GEM gpuva 锁。

此函数只能从非原子上下文中调用。

返回

如果 vm_bo 被销毁,则为 true,否则为 false。

struct drm_gpuvm_bo *drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm, struct drm_gem_object *obj)

查找给定 drm_gpuvmdrm_gem_objectdrm_gpuvm_bo

参数

struct drm_gpuvm *gpuvm

obj 在其中映射的 drm_gpuvm

struct drm_gem_object *obj

gpuvm 中映射的 drm_gem_object

描述

查找表示给定 drm_gpuvmdrm_gem_object 组合的 drm_gpuvm_bo。如果找到,则相应地增加 drm_gpuvm_bo 的引用计数。

返回

成功时指向 drm_gpuvm_bo 的指针,失败时为 NULL

struct drm_gpuvm_bo *drm_gpuvm_bo_obtain(struct drm_gpuvm *gpuvm, struct drm_gem_object *obj)

获取给定 drm_gpuvmdrm_gem_objectdrm_gpuvm_bo 实例。

参数

struct drm_gpuvm *gpuvm

obj 在其中映射的 drm_gpuvm

struct drm_gem_object *obj

gpuvm 中映射的 drm_gem_object

描述

查找表示给定 drm_gpuvmdrm_gem_object 组合的 drm_gpuvm_bo。如果找到,则相应地增加 drm_gpuvm_bo 的引用计数。如果未找到,则分配一个新的 drm_gpuvm_bo

一个新的 drm_gpuvm_bo 被添加到 GEM 的 gpuva 列表中。

返回

成功时返回指向 drm_gpuvm_bo 的指针,失败时返回 ERR_PTR。

struct drm_gpuvm_bo *drm_gpuvm_bo_obtain_prealloc(struct drm_gpuvm_bo *__vm_bo)

获取给定 drm_gpuvmdrm_gem_objectdrm_gpuvm_bo 实例。

参数

struct drm_gpuvm_bo *__vm_bo

一个预先分配的 struct drm_gpuvm_bo

描述

查找表示给定 drm_gpuvmdrm_gem_object 组合的 drm_gpuvm_bo。如果找到,则相应地增加找到的 drm_gpuvm_bo 的引用计数,同时减少 __vm_bo 的引用计数。如果未找到,则返回 __vm_bo,而无需进一步增加引用计数。

一个新的 drm_gpuvm_bo 被添加到 GEM 的 gpuva 列表中。

返回

如果未找到现有的 drm_gpuvm_bo,则返回指向找到的 drm_gpuvm_bo__vm_bo 的指针。

void drm_gpuvm_bo_extobj_add(struct drm_gpuvm_bo *vm_bo)

drm_gpuvm_bo 添加到其 drm_gpuvm 的 extobj 列表中。

参数

struct drm_gpuvm_bo *vm_bo

要添加到其 drm_gpuvm 的 extobj 列表中的 drm_gpuvm_bo

描述

如果给定的 vm_bo 尚未在列表中,并且对应的 drm_gem_object 实际上是一个外部对象,则将其添加到其 drm_gpuvm 的 extobj 列表中。

void drm_gpuvm_bo_evict(struct drm_gpuvm_bo *vm_bo, bool evict)

drm_gpuvm_bo 添加到 drm_gpuvms 的驱逐列表中或从中删除。

参数

struct drm_gpuvm_bo *vm_bo

要添加或删除的 drm_gpuvm_bo

bool evict

指示对象是否被驱逐。

描述

drm_gpuvm_bo 添加到 drm_gpuvms 的驱逐列表中或从中删除。

int drm_gpuva_insert(struct drm_gpuvm *gpuvm, struct drm_gpuva *va)

插入一个 drm_gpuva

参数

struct drm_gpuvm *gpuvm

要在其中插入 drm_gpuvadrm_gpuvm

struct drm_gpuva *va

要插入的 drm_gpuva

描述

将具有给定地址和范围的 drm_gpuva 插入到 drm_gpuvm 中。

使用 GPU VA 空间的迭代安全版本(例如 drm_gpuvm_for_each_va_safe()drm_gpuvm_for_each_va_range_safe())使用此函数是安全的。

返回

成功时为 0,失败时为负错误代码。

void drm_gpuva_remove(struct drm_gpuva *va)

删除一个 drm_gpuva

参数

struct drm_gpuva *va

要删除的 drm_gpuva

描述

这会从底层树中删除给定的 va

使用 GPU VA 空间的迭代安全版本(例如 drm_gpuvm_for_each_va_safe()drm_gpuvm_for_each_va_range_safe())使用此函数是安全的。

链接一个 drm_gpuva

参数

struct drm_gpuva *va

要链接的 drm_gpuva

struct drm_gpuvm_bo *vm_bo

要将 drm_gpuva 添加到的 drm_gpuvm_bo

描述

这会将给定的 va 添加到 drm_gpuvm_bo 的 GPU VA 列表中,并将 drm_gpuvm_bo 添加到其关联的 drm_gem_object 中。

对于添加到 drm_gpuvm_bo 的每个 drm_gpuva 条目,后者的引用计数会额外增加。

此函数期望调用者使用 GEM 的 dma_resv 锁或通过 drm_gem_gpuva_set_lock() 设置的驱动程序特定锁来保护 GEM 的 GPUVA 列表免受并发访问。

取消链接一个 drm_gpuva

参数

struct drm_gpuva *va

要取消链接的 drm_gpuva

描述

这将从与之关联的 drm_gem_object 的 GPU VA 列表中删除给定的 va

这将从 drm_gpuvm_bo 的 GPU VA 列表中删除给定的 va,并且在本次调用取消链接 drm_gpuvm_bo 中最后一个 drm_gpuva 的情况下,也会从与之关联的 drm_gem_object 中删除 drm_gpuvm_bo

对于从 drm_gpuvm_bo 中删除的每个 drm_gpuva 条目,都会删除后者的一个引用。

此函数期望调用者使用 GEM 的 dma_resv 锁或通过 drm_gem_gpuva_set_lock() 设置的驱动程序特定锁来保护 GEM 的 GPUVA 列表免受并发访问。

struct drm_gpuva *drm_gpuva_find_first(struct drm_gpuvm *gpuvm, u64 addr, u64 range)

查找给定范围内的第一个 drm_gpuva

参数

struct drm_gpuvm *gpuvm

要搜索的 drm_gpuvm

u64 addr

drm_gpuvas 的地址

u64 range

drm_gpuvas 的范围

返回

给定范围内的第一个 drm_gpuva

struct drm_gpuva *drm_gpuva_find(struct drm_gpuvm *gpuvm, u64 addr, u64 range)

查找一个 drm_gpuva

参数

struct drm_gpuvm *gpuvm

要搜索的 drm_gpuvm

u64 addr

drm_gpuvas 的地址

u64 range

drm_gpuvas 的范围

返回

在给定 addr 且具有给定 rangedrm_gpuva

struct drm_gpuva *drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start)

查找给定地址之前的 drm_gpuva

参数

struct drm_gpuvm *gpuvm

要搜索的 drm_gpuvm

u64 start

给定 GPU VA 的起始地址

描述

查找具有给定 start 地址的 GPU VA 之前的相邻 drm_gpuva

请注意,如果 GPU VA 映射之间有任何可用空间,则不会返回任何映射。

返回

指向找到的 drm_gpuva 的指针,如果没有找到则为 NULL

struct drm_gpuva *drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end)

查找给定地址之后的 drm_gpuva

参数

struct drm_gpuvm *gpuvm

要搜索的 drm_gpuvm

u64 end

给定 GPU VA 的结束地址

描述

查找具有给定 end 地址的 GPU VA 之后的相邻 drm_gpuva

请注意,如果 GPU VA 映射之间有任何可用空间,则不会返回任何映射。

返回

指向找到的 drm_gpuva 的指针,如果没有找到则为 NULL

bool drm_gpuvm_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range)

指示 VA 空间的给定间隔是否为空

参数

struct drm_gpuvm *gpuvm

要检查范围的 drm_gpuvm

u64 addr

范围的起始地址

u64 range

间隔的范围

返回

如果间隔为空则为 true,否则为 false

void drm_gpuva_map(struct drm_gpuvm *gpuvm, struct drm_gpuva *va, struct drm_gpuva_op_map *op)

根据 drm_gpuva_op_map 插入 drm_gpuva 的助手

参数

struct drm_gpuvm *gpuvm

drm_gpuvm

struct drm_gpuva *va

要插入的 drm_gpuva

struct drm_gpuva_op_map *op

用于初始化 vadrm_gpuva_op_map

描述

op 初始化 va 并将其插入到给定的 gpuvm 中。

void drm_gpuva_remap(struct drm_gpuva *prev, struct drm_gpuva *next, struct drm_gpuva_op_remap *op)

根据 drm_gpuva_op_remap 重新映射 drm_gpuva 的助手

参数

struct drm_gpuva *prev

在保持映射的起始位置时要重新映射的 drm_gpuva

struct drm_gpuva *next

在保持映射的结束位置时要重新映射的 drm_gpuva

struct drm_gpuva_op_remap *op

使用 drm_gpuva_op_remap 初始化 prevnext

描述

移除当前已映射的 drm_gpuva,并使用 prev 和/或 next 重新映射它。

void drm_gpuva_unmap(struct drm_gpuva_op_unmap *op)

根据 drm_gpuva_op_unmap 移除 drm_gpuva 的辅助函数

参数

struct drm_gpuva_op_unmap *op

指定要移除的 drm_gpuvadrm_gpuva_op_unmap

描述

移除与 drm_gpuva_op_unmap 关联的 drm_gpuva

int drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, void *priv, u64 req_addr, u64 req_range, struct drm_gem_object *req_obj, u64 req_offset)

创建 drm_gpuva_op 分割/合并步骤

参数

struct drm_gpuvm *gpuvm

表示 GPU VA 空间的 drm_gpuvm

void *priv

指向驱动程序私有数据结构的指针

u64 req_addr

新映射的起始地址

u64 req_range

新映射的范围

struct drm_gem_object *req_obj

要映射的 drm_gem_object

u64 req_offset

drm_gem_object 中的偏移量

描述

此函数遍历 GPU VA 空间的给定范围。它利用 drm_gpuvm_ops 回调到驱动程序,提供分割和合并步骤。

驱动程序可以使用这些回调立即在回调中更新 GPU VA 空间。如果驱动程序决定复制并存储操作以供稍后处理,则在 drm_gpuvm 对 GPU VA 空间的视图使用上一组操作进行更新之前,不允许调用此函数或 drm_gpuvm_sm_unmap。要更新 drm_gpuvm 的 GPU VA 空间视图,应使用 drm_gpuva_insert()、drm_gpuva_destroy_locked() 和/或 drm_gpuva_destroy_unlocked()。

回调序列可以包含 map、unmap 和 remap 操作,但如果不需要操作,例如,如果请求的映射已经以完全相同的方式存在,则回调序列也可能为空。

可以有任意数量的 unmap 操作,最多两个 remap 操作和一个 map 操作。后者表示调用者请求的原始 map 操作。

返回

成功时返回 0 或负错误代码

int drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, void *priv, u64 req_addr, u64 req_range)

创建用于在 unmap 时分割的 drm_gpuva_ops

参数

struct drm_gpuvm *gpuvm

表示 GPU VA 空间的 drm_gpuvm

void *priv

指向驱动程序私有数据结构的指针

u64 req_addr

要取消映射的范围的起始地址

u64 req_range

要取消映射的映射范围

描述

此函数遍历 GPU VA 空间的给定范围。它利用 drm_gpuvm_ops 回调到驱动程序,提供用于取消映射的操作,并在需要时分割现有的映射。

驱动程序可以使用这些回调立即在回调中更新 GPU VA 空间。如果驱动程序决定复制并存储操作以供稍后处理,则在 drm_gpuvm 对 GPU VA 空间的视图使用上一组操作进行更新之前,不允许调用此函数或 drm_gpuvm_sm_map。要更新 drm_gpuvm 的 GPU VA 空间视图,应使用 drm_gpuva_insert()、drm_gpuva_destroy_locked() 和/或 drm_gpuva_destroy_unlocked()。

回调序列可以包含 unmap 和 remap 操作,具体取决于是否存在需要分割的实际重叠映射。

可以有任意数量的 unmap 操作,以及最多两个 remap 操作。

返回

成功时返回 0 或负错误代码

struct drm_gpuva_ops *drm_gpuvm_sm_map_ops_create(struct drm_gpuvm *gpuvm, u64 req_addr, u64 req_range, struct drm_gem_object *req_obj, u64 req_offset)

创建用于分割和合并的 drm_gpuva_ops

参数

struct drm_gpuvm *gpuvm

表示 GPU VA 空间的 drm_gpuvm

u64 req_addr

新映射的起始地址

u64 req_range

新映射的范围

struct drm_gem_object *req_obj

要映射的 drm_gem_object

u64 req_offset

drm_gem_object 中的偏移量

描述

此函数创建要执行的操作列表,以将现有映射与新请求的映射进行分割和合并。

可以使用 drm_gpuva_for_each_op 遍历列表,并且必须按给定的顺序处理列表。它可以包含 map、unmap 和 remap 操作,但如果不需要操作,例如,如果请求的映射已经以完全相同的方式存在,则它也可能为空。

可以有任意数量的 unmap 操作,最多两个 remap 操作和一个 map 操作。后者表示调用者请求的原始 map 操作。

请注意,在再次使用另一个映射请求调用此函数之前,必须更新 drm_gpuvm 对 GPU VA 空间的视图。先前获得的操作必须被处理或放弃。要更新 drm_gpuvm 的 GPU VA 空间视图,应使用 drm_gpuva_insert()、drm_gpuva_destroy_locked() 和/或 drm_gpuva_destroy_unlocked()。

在调用者完成处理返回的 drm_gpuva_ops 后,必须使用 drm_gpuva_ops_free 释放它们。

返回

成功时返回指向 drm_gpuva_ops 的指针,失败时返回 ERR_PTR

struct drm_gpuva_ops *drm_gpuvm_sm_unmap_ops_create(struct drm_gpuvm *gpuvm, u64 req_addr, u64 req_range)

创建用于在 unmap 时分割的 drm_gpuva_ops

参数

struct drm_gpuvm *gpuvm

表示 GPU VA 空间的 drm_gpuvm

u64 req_addr

要取消映射的范围的起始地址

u64 req_range

要取消映射的映射范围

描述

此函数创建一个操作列表,用于执行取消映射,并且如果需要,还可以拆分与取消映射范围重叠的映射。

该列表可以使用 drm_gpuva_for_each_op 进行迭代,并且必须按给定的顺序进行处理。根据是否存在要拆分的实际重叠映射,它可以包含取消映射和重新映射操作。

可以有任意数量的 unmap 操作,以及最多两个 remap 操作。

请注意,在再次调用此函数并使用另一个取消映射的范围之前,必须更新 drm_gpuvm 的 GPU VA 空间视图。之前获得的操作必须被处理或放弃。要更新 drm_gpuvm 的 GPU VA 空间视图,应使用 drm_gpuva_insert()、drm_gpuva_destroy_locked() 和/或 drm_gpuva_destroy_unlocked()。

在调用者完成处理返回的 drm_gpuva_ops 后,必须使用 drm_gpuva_ops_free 释放它们。

返回

成功时返回指向 drm_gpuva_ops 的指针,失败时返回 ERR_PTR

struct drm_gpuva_ops *drm_gpuvm_prefetch_ops_create(struct drm_gpuvm *gpuvm, u64 addr, u64 range)

创建要预取的 drm_gpuva_ops

参数

struct drm_gpuvm *gpuvm

表示 GPU VA 空间的 drm_gpuvm

u64 addr

要预取的范围的起始地址

u64 range

要预取的映射的范围

描述

此函数创建一个操作列表,用于执行预取。

该列表可以使用 drm_gpuva_for_each_op 进行迭代,并且必须按给定的顺序进行处理。它可以包含预取操作。

可以有任意数量的预取操作。

在调用者完成处理返回的 drm_gpuva_ops 后,必须使用 drm_gpuva_ops_free 释放它们。

返回

成功时返回指向 drm_gpuva_ops 的指针,失败时返回 ERR_PTR

struct drm_gpuva_ops *drm_gpuvm_bo_unmap_ops_create(struct drm_gpuvm_bo *vm_bo)

创建用于取消映射 GEM 的 drm_gpuva_ops

参数

struct drm_gpuvm_bo *vm_bo

drm_gpuvm_bo 抽象

描述

此函数创建一个操作列表,用于执行与 GEM 关联的每个 GPUVA 的取消映射。

该列表可以使用 drm_gpuva_for_each_op 进行迭代,并且包含任意数量的取消映射操作。

在调用者完成处理返回的 drm_gpuva_ops 后,必须使用 drm_gpuva_ops_free 释放它们。

调用者有责任使用 GEM 的 dma_resv 锁来保护 GEM 的 GPUVA 列表免受并发访问。

返回

成功时返回指向 drm_gpuva_ops 的指针,失败时返回 ERR_PTR

void drm_gpuva_ops_free(struct drm_gpuvm *gpuvm, struct drm_gpuva_ops *ops)

释放给定的 drm_gpuva_ops

参数

struct drm_gpuvm *gpuvm

创建 ops 的 drm_gpuvm

struct drm_gpuva_ops *ops

要释放的 drm_gpuva_ops

描述

释放给定的 drm_gpuva_ops 结构,包括与之关联的所有操作。

DRM Buddy 分配器

DRM Buddy 函数参考

int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)

初始化内存管理器

参数

struct drm_buddy *mm

要初始化的 DRM buddy 管理器

u64 size

要管理的字节大小

u64 chunk_size

我们分配的最小页面大小(以字节为单位)

描述

初始化内存管理器及其资源。

返回

成功返回 0,失败返回错误代码。

void drm_buddy_fini(struct drm_buddy *mm)

关闭内存管理器

参数

struct drm_buddy *mm

要释放的 DRM buddy 管理器

描述

清理内存管理器资源和空闲列表

struct drm_buddy_block *drm_get_buddy(struct drm_buddy_block *block)

获取伙伴地址

参数

struct drm_buddy_block *block

DRM buddy 块

描述

返回 block 的相应伙伴块,如果这是一个根块并且不能进一步合并,则返回 NULL。需要某种类型的锁定来防止任何并发的分配和释放操作。

void drm_buddy_free_block(struct drm_buddy *mm, struct drm_buddy_block *block)

释放一个块

参数

struct drm_buddy *mm

DRM buddy 管理器

struct drm_buddy_block *block

要释放的块

void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects, unsigned int flags)

释放块

参数

struct drm_buddy *mm

DRM buddy 管理器

struct list_head *objects

要释放块的输入列表头

unsigned int flags

可选标志,如 DRM_BUDDY_CLEARED

int drm_buddy_block_trim(struct drm_buddy *mm, u64 *start, u64 new_size, struct list_head *blocks)

释放未使用的页面

参数

struct drm_buddy *mm

DRM buddy 管理器

u64 *start

开始修剪的起始地址。

u64 new_size

原始请求的大小

struct list_head *blocks

已分配块的输入和输出列表。必须包含单个块作为要修剪的输入。成功后,将包含构成 new_size 的新分配的块。块始终按升序排列

描述

对于连续分配,我们会将大小向上取整到最接近的 2 的幂的值,驱动程序会消耗实际大小,因此剩余部分未使用,可以使用此函数选择性地释放。

返回

成功返回 0,失败返回错误代码。

int drm_buddy_alloc_blocks(struct drm_buddy *mm, u64 start, u64 end, u64 size, u64 min_block_size, struct list_head *blocks, unsigned long flags)

分配 2 的幂的块

参数

struct drm_buddy *mm

用于分配的 DRM 伙伴管理器

u64 start

此块的允许范围的起始位置

u64 end

此块的允许范围的结束位置

u64 size

以字节为单位的分配大小

u64 min_block_size

分配的对齐方式

struct list_head *blocks

添加分配块的输出列表头

unsigned long flags

DRM_BUDDY_*_ALLOCATION 标志

描述

在范围限制上调用 alloc_range_bias(),它会遍历树并返回所需的块。

没有强制执行范围限制时调用 alloc_from_freelist(),它会从空闲列表中选择块。

返回

成功返回 0,失败返回错误代码。

void drm_buddy_block_print(struct drm_buddy *mm, struct drm_buddy_block *block, struct drm_printer *p)

打印块信息

参数

struct drm_buddy *mm

DRM buddy 管理器

struct drm_buddy_block *block

DRM buddy 块

struct drm_printer *p

要使用的 DRM 打印机

void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p)

打印分配器状态

参数

struct drm_buddy *mm

DRM buddy 管理器

struct drm_printer *p

要使用的 DRM 打印机

DRM 缓存处理和快速 WC memcpy()

void drm_clflush_pages(struct page *pages[], unsigned long num_pages)

刷新一组页面的数据缓存行。

参数

struct page *pages[]

要刷新的页面列表。

unsigned long num_pages

数组中的页数。

描述

刷新指向数组中页面的地址的每个数据缓存行条目。

void drm_clflush_sg(struct sg_table *st)

刷新指向散布聚集的数据缓存行。

参数

struct sg_table *st

struct sg_table。

描述

刷新指向 sg 中地址的每个数据缓存行条目。

void drm_clflush_virt_range(void *addr, unsigned long length)

刷新区域的数据缓存行

参数

void *addr

初始内核内存地址。

unsigned long length

区域大小。

描述

刷新指向所请求区域中的地址的每个数据缓存行条目。

void drm_memcpy_from_wc(struct iosys_map *dst, const struct iosys_map *src, unsigned long len)

从可能是 WC 的源执行最快的可用 memcpy。

参数

struct iosys_map *dst

目标指针

const struct iosys_map *src

源指针

unsigned long len

要传输的区域的大小(以字节为单位)

描述

尝试使用 arch 优化的 memcpy,用于从 WC 区域进行预取读取,如果没有此类可用,则回退到正常的 memcpy。

DRM 同步对象

DRM 同步对象(syncobj,请参阅结构体 drm_syncobj)为同步原语提供一个容器,用户空间可以使用它来显式同步 GPU 命令,可以在用户空间进程之间共享,并且可以在不同的 DRM 驱动程序之间共享。它们的主要用例是实现 Vulkan 栅栏和信号量。syncobj 用户空间 API 提供了几个操作的 ioctl:

  • 创建和销毁同步对象

  • 将同步对象导入/导出到/从同步对象文件描述符

  • 将同步对象的底层栅栏导入/导出到/从同步文件

  • 重置同步对象(将其栅栏设置为 NULL)

  • 发送信号给同步对象(设置一个轻易发送信号的栅栏)

  • 等待同步对象的栅栏出现并发送信号

syncobj 用户空间 API 还提供了用于操作同步对象的操作,其操作是根据 dma_fence_chain 结构体的时间线而不是单个 dma_fence 结构体,通过以下操作:

  • 发送时间线上给定点的信号

  • 等待给定点出现和/或发送信号

  • 从/到时间线的给定点导入和导出

其核心是一个同步对象,它只是一个指向 dma_fence 结构体的指针的包装器,该指针可能为 NULL。当首次创建同步对象时,其指针要么为 NULL,要么是指向已发送信号的栅栏的指针,具体取决于是否将 DRM_SYNCOBJ_CREATE_SIGNALED 标志传递给 DRM_IOCTL_SYNCOBJ_CREATE

如果同步对象被视为二进制原语(其状态为已发送信号或未发送信号),则当 DRM 驱动程序中入队 GPU 工作以发送同步对象信号时,同步对象的栅栏将替换为将通过该工作完成发送信号的栅栏。如果将同步对象视为时间线原语,则当 DRM 驱动程序中入队 GPU 工作以发送同步对象的给定点的信号时,将创建一个新的 dma_fence_chain 结构体,该结构体指向 DRM 驱动程序的栅栏,并且还指向同步对象中之前的栅栏。新的 dma_fence_chain 栅栏将替换同步对象的栅栏,并且将通过 DRM 驱动程序的工作完成和与之前在同步对象中的栅栏关联的任何工作来发送信号。

当在 DRM 驱动程序中入队等待同步对象的 GPU 工作时,在入队工作时,它会在将工作提交到硬件之前等待同步对象的栅栏。该栅栏为:

  • 如果将同步对象视为二进制原语,则为同步对象的当前栅栏。

  • 如果将同步对象视为时间线原语,则为与给定点关联的 dma_fence 结构体。

如果同步对象的栅栏为 NULL 或不存在于同步对象的时间线中,则入队操作应失败。

使用二进制同步对象,所有对同步对象的栅栏的操作都是根据用户空间调用 ioctl 时的当前栅栏进行的,无论该操作是立即主机端操作(发送信号或重置)还是在某些驱动程序队列中入队的操作。DRM_IOCTL_SYNCOBJ_RESETDRM_IOCTL_SYNCOBJ_SIGNAL 可用于通过将指针重置为 NULL 或将其指针设置为已发送信号的栅栏来从主机操作同步对象。

对于时间线同步对象,对同步对象的 fence 的所有操作都以引用时间线上点的 u64 值进行。请参阅 dma_fence_chain_find_seqno(),了解如何在时间线中找到给定的点。

请注意,当处理被视为时间线的同步对象时,应用程序应始终小心使用时间线 ioctl() 集。对被视为时间线的同步对象使用二进制 ioctl() 集可能会导致不正确的同步。通过使用点值为 0 的时间线 ioctl() 集支持使用二进制同步对象,这将重现二进制 ioctl() 集的行为(例如,在发出信号时替换同步对象的 fence)。

主机端等待同步对象

DRM_IOCTL_SYNCOBJ_WAIT 接受一个同步对象句柄数组,并在所有同步对象 fence 上同时执行主机端等待。如果设置了 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL,则等待 ioctl 将等待所有同步对象 fence 都发出信号后才返回。否则,它将在至少一个同步对象 fence 发出信号后返回,并将发出信号的 fence 的索引写回客户端。

与在同步对象中看到 NULL fence 时会失败的排队 GPU 工作依赖项不同,如果设置了 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,则主机端等待将首先等待同步对象接收非 NULL fence,然后等待该 fence。如果未设置 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,并且数组中的任何一个同步对象具有 NULL fence,则将返回 -EINVAL。假设同步对象以 NULL fence 开始,这允许客户端在一个线程(或进程)中执行主机等待,该线程(或进程)等待在另一个线程(或进程)中提交的 GPU 工作,而无需在两者之间手动同步。此要求继承自 Vulkan fence API。

如果设置了 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE,则 ioctl 还将在等待之前在后备 fence 上设置一个 fence 截止日期提示,以便为 fence 发送者提供适当的紧迫感。截止日期在纳秒单位中指定为绝对 CLOCK_MONOTONIC 值。

类似地,DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT 接受一个同步对象句柄数组以及一个 u64 点数组,并在所有同步对象 fence 的给定点上同时执行主机端等待。

DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT 还增加了等待给定 fence 在时间线上实体化而不等待 fence 发出信号的能力,方法是使用 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE 标志。此要求继承自 Vulkan 时间线信号量 API 所需的等待后发送信号行为。

或者,可以使用 DRM_IOCTL_SYNCOBJ_EVENTFD 在不阻塞的情况下等待:当同步对象发出信号时,将发出一个 eventfd 信号。这有助于将等待集成到事件循环中。

同步对象的导入/导出

DRM_IOCTL_SYNCOBJ_FD_TO_HANDLEDRM_IOCTL_SYNCOBJ_HANDLE_TO_FD 提供了两种导入/导出同步对象的机制。

第一种机制允许客户端将整个同步对象导入或导出到文件描述符。这些 fd 是不透明的,除了在进程之间传递同步对象外,没有其他用例。所有导出的文件描述符以及通过导入这些文件描述符而创建的任何同步对象句柄都拥有对同一个底层结构 drm_syncobj 的引用,并且同步对象可以在与其共享的所有进程中持久使用。只有在最后一个引用被删除后,同步对象才会被释放。与 dma-buf 不同,导入同步对象会为每次导入创建一个新的句柄(及其自身的引用),而不是重复数据删除。这种持久导入/导出的主要用例是共享 Vulkan fence 和信号量。

第二种导入/导出机制,由 DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILEDRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE 指示,允许客户端从/向 sync_file 导入/导出同步对象的当前 fence。当同步对象导出到 sync 文件时,该 sync 文件会包装导出时同步对象的 fence,并且对同步对象的任何后续信号或重置操作都不会影响导出的 sync 文件。当 sync 文件导入到同步对象时,同步对象的 fence 将设置为该 sync 文件包装的 fence。由于 sync 文件是不可变的,因此重置或发出同步对象信号不会影响任何已将 fence 导入到同步对象的 sync 文件。

时间线同步对象中时间线点的导入/导出

DRM_IOCTL_SYNCOBJ_TRANSFER 提供了一种机制,用于将给定 u64 点的同步对象的结构 dma_fence_chain 传输到另一个同步对象中的另一个 u64 点。

请注意,如果要将结构 dma_fence_chain 从时间线同步对象上的给定点传输到/从二进制同步对象传输,则可以使用点 0 表示获取/替换同步对象中的 fence。

struct drm_syncobj

同步对象。

定义:

struct drm_syncobj {
    struct kref refcount;
    struct dma_fence __rcu *fence;
    struct list_head cb_list;
    struct list_head ev_fd_list;
    spinlock_t lock;
    struct file *file;
};

成员

refcount

此对象的引用计数。

fence

NULL 或指向绑定到此对象的 fence 的指针。

此字段不应直接使用。请改用 drm_syncobj_fence_get()drm_syncobj_replace_fence()

cb_list

fence 被替换时要调用的回调列表。

ev_fd_list

注册的 eventfd 列表。

保护 cb_listev_fd_list,并写锁定 fence

file

此同步对象的后备文件。

描述

此结构定义了一个通用同步对象,它包装了一个 dma_fence

void drm_syncobj_get(struct drm_syncobj *obj)

获取同步对象引用

参数

struct drm_syncobj *obj

同步对象

描述

这会获取对 obj 的额外引用。在没有已经持有引用的情况下调用此函数是非法的。无需锁定。

void drm_syncobj_put(struct drm_syncobj *obj)

释放对同步对象的引用。

参数

struct drm_syncobj *obj

同步对象。

struct dma_fence *drm_syncobj_fence_get(struct drm_syncobj *syncobj)

获取对同步对象中 fence 的引用

参数

struct drm_syncobj *syncobj

同步对象。

描述

如果不是 NULL,则会获取对 obj 中包含的 drm_syncobj.fence 的额外引用。在没有已经持有引用的情况下调用此函数是非法的。无需锁定。

返回

obj 的 fence,如果没有,则为 NULL。

struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, u32 handle)

查找并引用同步对象。

参数

struct drm_file *file_private

drm 文件私有指针

u32 handle

要查找的同步对象句柄。

描述

返回指向句柄所指向的同步对象的引用,或 NULL。必须通过调用 drm_syncobj_put() 来释放引用。

void drm_syncobj_add_point(struct drm_syncobj *syncobj, struct dma_fence_chain *chain, struct dma_fence *fence, uint64_t point)

向 syncobj 添加新的时间线点

参数

struct drm_syncobj *syncobj

要添加时间线点的同步对象

struct dma_fence_chain *chain

用于添加点的链节点

struct dma_fence *fence

要封装在链节点中的 fence

uint64_t point

该点使用的序列号

描述

将链节点作为新的时间线点添加到 syncobj。

void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, struct dma_fence *fence)

替换同步对象中的 fence。

参数

struct drm_syncobj *syncobj

要替换 fence 的同步对象

struct dma_fence *fence

要安装在同步文件中的 fence。

描述

这将替换同步对象上的 fence。

int drm_syncobj_find_fence(struct drm_file *file_private, u32 handle, u64 point, u64 flags, struct dma_fence **fence)

查找并引用同步对象中的 fence

参数

struct drm_file *file_private

drm 文件私有指针

u32 handle

要查找的同步对象句柄。

u64 point

时间线点

u64 flags

DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT 或不等待

struct dma_fence **fence

fence 的输出参数

描述

这只是一个方便的函数,它结合了 drm_syncobj_find()drm_syncobj_fence_get()

成功时返回 0,失败时返回负错误值。成功时,fence 包含对 fence 的引用,必须通过调用 dma_fence_put() 来释放。

void drm_syncobj_free(struct kref *kref)

释放同步对象。

参数

struct kref *kref

要释放的 kref。

描述

只能从 drm_syncobj_put 中的 kref_put 调用。

int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, struct dma_fence *fence)

创建一个新的 syncobj

参数

struct drm_syncobj **out_syncobj

返回的 syncobj

uint32_t flags

DRM_SYNCOBJ_* 标志

struct dma_fence *fence

如果非 NULL,则 syncobj 将表示此 fence

描述

这是创建同步对象的第一个函数。创建后,驱动程序可能希望通过 drm_syncobj_get_handle()drm_syncobj_get_fd() 使其对用户空间可用。

成功时返回 0,失败时返回负错误值。

int drm_syncobj_get_handle(struct drm_file *file_private, struct drm_syncobj *syncobj, u32 *handle)

从 syncobj 获取句柄

参数

struct drm_file *file_private

drm 文件私有指针

struct drm_syncobj *syncobj

要导出的同步对象

u32 *handle

带有新句柄的输出参数

描述

将使用 drm_syncobj_create() 创建的同步对象作为 file_private 上的句柄导出到用户空间。

成功时返回 0,失败时返回负错误值。

int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd)

从 syncobj 获取文件描述符

参数

struct drm_syncobj *syncobj

要导出的同步对象

int *p_fd

带有新文件描述符的输出参数

描述

将使用 drm_syncobj_create() 创建的同步对象作为文件描述符导出。

成功时返回 0,失败时返回负错误值。

signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)

从绝对值计算 jiffies 超时

参数

int64_t timeout_nsec

以纳秒为单位的超时纳秒分量,轮询时为 0

描述

从秒/纳秒的绝对时间计算 jiffies 的超时时间。

DRM 执行上下文

此组件主要抽象了在准备硬件操作(例如,命令提交、页表更新等)时锁定多个 GEM 对象所必需的重试循环。

如果在锁定 GEM 对象时检测到争用,则清理过程会解锁所有先前锁定的 GEM 对象,并首先锁定争用的对象,然后再锁定任何其他对象。

在锁定对象后,可以选择在 GEM 对象内的 dma_resv 对象上保留 fence 插槽。

典型的用法模式应如下所示

struct drm_gem_object *obj;
struct drm_exec exec;
unsigned long index;
int ret;

drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
drm_exec_until_all_locked(&exec) {
        ret = drm_exec_prepare_obj(&exec, boA, 1);
        drm_exec_retry_on_contention(&exec);
        if (ret)
                goto error;

        ret = drm_exec_prepare_obj(&exec, boB, 1);
        drm_exec_retry_on_contention(&exec);
        if (ret)
                goto error;
}

drm_exec_for_each_locked_object(&exec, index, obj) {
        dma_resv_add_fence(obj->resv, fence, DMA_RESV_USAGE_READ);
        ...
}
drm_exec_fini(&exec);

有关更多详细信息,请参见 struct dma_exec。

struct drm_exec

执行上下文

定义:

struct drm_exec {
    u32 flags;
    struct ww_acquire_ctx   ticket;
    unsigned int            num_objects;
    unsigned int            max_objects;
    struct drm_gem_object   **objects;
    struct drm_gem_object   *contended;
    struct drm_gem_object *prelocked;
};

成员

flags

控制锁定行为的标志

票证

用于获取锁定的 WW 票证

num_objects

锁定的对象数

max_objects

数组中的最大对象数

objects

锁定的对象数组

contended

我们为之退出的争用的 GEM 对象

prelocked

由于争用而预先锁定的 GEM 对象

struct drm_gem_object *drm_exec_obj(struct drm_exec *exec, unsigned long index)

返回给定 drm_exec 索引的对象

参数

struct drm_exec *exec

指向 drm_exec 上下文的指针

unsigned long index

索引。

返回

如果索引在已锁定对象的数量范围内,则指向与 index 对应的已锁定对象的指针。否则为 NULL。

drm_exec_for_each_locked_object

drm_exec_for_each_locked_object (exec, index, obj)

遍历所有已锁定的对象

参数

exec

drm_exec 对象

索引

用于迭代的 unsigned long 索引

obj

当前的 GEM 对象

描述

遍历 drm_exec 对象内的所有已锁定 GEM 对象。

drm_exec_for_each_locked_object_reverse

drm_exec_for_each_locked_object_reverse (exec, index, obj)

按反向锁定顺序遍历所有已锁定的对象

参数

exec

drm_exec 对象

索引

用于迭代的 unsigned long 索引

obj

当前的 GEM 对象

描述

按反向锁定顺序遍历 drm_exec 对象内的所有已锁定 GEM 对象。请注意,index 可能会低于零并回绕,但这将被 drm_exec_obj() 捕获,从而返回一个 NULL 对象。

drm_exec_until_all_locked

drm_exec_until_all_locked (exec)

循环直到所有 GEM 对象都被锁定

参数

exec

drm_exec 对象

描述

drm_exec 对象的的核心功能。循环直到所有 GEM 对象都被锁定并且不存在更多争用。在循环开始时,保证没有 GEM 对象被锁定。

由于标签不能在循环体本地定义,我们使用跳转指针来确保重试仅在循环体内使用。

drm_exec_retry_on_contention

drm_exec_retry_on_contention (exec)

重新启动循环以获取所有锁

参数

exec

drm_exec 对象

描述

当检测到争用,并且我们需要清理并重新启动循环以准备所有 GEM 对象时,控制流助手用于继续。

bool drm_exec_is_contended(struct drm_exec *exec)

检查争用

参数

struct drm_exec *exec

drm_exec 对象

描述

如果 drm_exec 对象在锁定 GEM 对象时遇到一些争用并且需要清理,则返回 true。

void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr)

初始化 drm_exec 对象

参数

struct drm_exec *exec

要初始化的 drm_exec 对象

u32 flags

控制锁定行为,请参阅 DRM_EXEC_* 定义

unsigned nr

初始对象数量

描述

初始化对象并确保我们可以跟踪已锁定的对象。

如果 nr 非零,则将其用作初始对象表大小。无论哪种情况,表都将按需增长(重新分配)。

void drm_exec_fini(struct drm_exec *exec)

完成 drm_exec 对象

参数

struct drm_exec *exec

要完成的 drm_exec 对象

描述

解锁所有已锁定的对象,删除对对象的引用,并释放用于跟踪状态的所有内存。

bool drm_exec_cleanup(struct drm_exec *exec)

检测到争用时进行清理

参数

struct drm_exec *exec

要清理的 drm_exec 对象

描述

清理当前状态,如果应该停留在重试循环中则返回 true,如果未检测到任何争用并且可以保持对象锁定,则返回 false。

int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)

锁定 GEM 对象以供使用

参数

struct drm_exec *exec

具有状态的 drm_exec 对象

struct drm_gem_object *obj

要锁定的 GEM 对象

描述

锁定 GEM 对象以供使用并获取对其的引用。

返回

如果检测到争用则返回 -EDEADLK,如果对象已被锁定则返回 -EALREADY(可以通过设置 DRM_EXEC_IGNORE_DUPLICATES 标志来禁止),如果内存分配失败则返回 -ENOMEM,成功则返回零。

void drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj)

在此执行上下文中解锁 GEM 对象

参数

struct drm_exec *exec

具有状态的 drm_exec 对象

struct drm_gem_object *obj

要解锁的 GEM 对象

描述

解锁 GEM 对象并将其从已锁定对象的集合中删除。应仅用于解锁最近锁定的对象。解锁很久以前锁定的对象效率不高。

int drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj, unsigned int num_fences)

准备 GEM 对象以供使用

参数

struct drm_exec *exec

具有状态的 drm_exec 对象

struct drm_gem_object *obj

要准备的 GEM 对象

unsigned int num_fences

要保留的栅栏数量

描述

通过锁定并保留栅栏槽来准备 GEM 对象以供使用。

返回

如果检测到争用则返回 -EDEADLK,如果对象已被锁定则返回 -EALREADY,如果内存分配失败则返回 -ENOMEM,成功则返回零。

int drm_exec_prepare_array(struct drm_exec *exec, struct drm_gem_object **objects, unsigned int num_objects, unsigned int num_fences)

用于准备对象数组的助手

参数

struct drm_exec *exec

具有状态的 drm_exec 对象

struct drm_gem_object **objects

要准备的 GEM 对象数组

unsigned int num_objects

数组中 GEM 对象的数量

unsigned int num_fences

要在每个 GEM 对象上保留的栅栏数量

描述

准备数组中的所有 GEM 对象,在第一个错误时中止。锁定每个 GEM 对象后,在其上保留 num_fences 个栅栏。

返回

争用时返回 -EDEADLOCK,如果对象已被锁定则返回 -EALREADY,如果内存分配失败则返回 -ENOMEM,成功则返回零。

GPU 调度器

概述

GPU 调度器提供实体,允许用户空间将作业推送到软件队列中,然后这些作业在硬件运行队列上进行调度。软件队列之间具有优先级。调度器使用 FIFO 从运行队列中选择实体。调度器提供作业之间的依赖关系处理功能。驱动程序应该为调度器的后端操作提供回调函数,例如将作业提交到硬件运行队列、返回作业的依赖关系等。

调度器的组织如下

  1. 每个硬件运行队列都有一个调度器

  2. 每个调度器都有多个具有不同优先级的运行队列(例如,HIGH_HW、HIGH_SW、KERNEL、NORMAL)

  3. 每个调度器运行队列都有一个要调度的实体队列

  4. 实体本身维护一个将在硬件上调度的作业队列。

实体中的作业始终按照它们被推送的顺序进行调度。

请注意,一旦作业从实体队列中取出并推送到硬件(即,挂起队列),则不得再通过作业实体指针引用该实体。

流控制

DRM GPU 调度器提供了一种流控制机制,用于调节从调度器实体获取的作业的执行速率。

在此上下文中,drm_gpu_scheduler 跟踪驱动程序指定的信用额度,该信用额度表示此调度器的容量,以及一个信用计数;每个 drm_sched_job 携带驱动程序指定的信用数量。

一旦作业被执行(但尚未完成),作业的信用额度将计入调度器的信用计数,直到作业完成。如果通过执行另一个作业,调度器的信用计数将超过调度器的信用额度,则该作业将不会被执行。相反,调度器将等待信用计数减少到足以不超出其信用额度的程度。这意味着需要等待先前执行的作业。

可选地,驱动程序可以注册一个回调函数(update_job_credits),该函数由 struct drm_sched_backend_ops 提供,以动态更新作业的信用额度。调度器每次考虑执行作业时都会执行此回调函数,并随后检查作业是否符合调度器的信用额度限制。

调度器函数引用

DRM_SCHED_FENCE_DONT_PIPELINE

DRM_SCHED_FENCE_DONT_PIPELINE

防止依赖流水线

描述

在调度器栅栏上设置此标志可防止依赖此栅栏的作业进行流水线处理。换句话说,在将依赖作业推送到硬件队列之前,我们始终会插入一个完整的 CPU 往返。

DRM_SCHED_FENCE_FLAG_HAS_DEADLINE_BIT

DRM_SCHED_FENCE_FLAG_HAS_DEADLINE_BIT

已设置栅栏截止时间提示

描述

因为我们可以在创建后备硬件栅栏之前设置截止时间提示,所以我们需要跟踪是否已设置截止时间。

struct drm_sched_entity

作业队列的包装器(通常附加到 DRM file_priv)。

定义:

struct drm_sched_entity {
    struct list_head                list;
    spinlock_t lock;
    struct drm_sched_rq             *rq;
    struct drm_gpu_scheduler        **sched_list;
    unsigned int                    num_sched_list;
    enum drm_sched_priority         priority;
    struct spsc_queue               job_queue;
    atomic_t fence_seq;
    uint64_t fence_context;
    struct dma_fence                *dependency;
    struct dma_fence_cb             cb;
    atomic_t *guilty;
    struct dma_fence __rcu          *last_scheduled;
    struct task_struct              *last_user;
    bool stopped;
    struct completion               entity_idle;
    ktime_t oldest_job_waiting;
    struct rb_node                  rb_tree_node;
};

成员

list

用于将此结构附加到 drm_sched_rq.entities 下的运行队列 rq 中的实体列表。

rqdrm_sched_rq.lock 保护。

保护此实体所属的运行队列 (rq)、priority 和调度器列表(sched_listnum_sched_list)的锁。

rq

此实体当前被调度的运行队列。

FIXME:此锁的锁定非常不明确。写入器受 lock 保护,但读取器通常是无锁的,并且似乎只是在没有 READ_ONCE 的情况下竞争。

sched_list

调度器列表(struct drm_gpu_scheduler)。此实体中的作业可以调度到此列表中的任何调度器上。

可以通过调用 drm_sched_entity_modify_sched() 来修改此项。锁定完全由驱动程序决定,有关详细信息,请参阅上面的函数。

如果 num_sched_list 等于 1 且已设置 rq,则此项将设置为 NULL。

FIXME:这意味着在这种情况下,通过 drm_sched_entity_set_priority() 更改的优先级将丢失。

num_sched_list

sched_list 中的 drm_gpu_scheduler 的数量。

priority

实体的优先级。可以通过调用 drm_sched_entity_set_priority() 来修改此项。受 lock 保护。

job_queue

此实体的作业列表。

fence_seq

一个线性递增的序列号,每个属于实体的新的 drm_sched_fence 都会递增。

FIXME:drm_sched_job_arm() 的调用者需要确保正确的锁定,这不需要是原子操作。

fence_context

属于此实体的所有栅栏的唯一上下文。drm_sched_fence.scheduled 使用 fence_context,但 drm_sched_fence.finished 使用 fence_context + 1。

dependency

作业队列顶部的作业的依赖栅栏。

cb

上述依赖栅栏的回调。

guilty

指向实体的 guilty。

last_scheduled

指向上次调度的作业的已完成栅栏。仅由调度器线程写入,如果队列为空,则可以从 drm_sched_job_arm() 无锁访问。

last_user

最后一个将作业推送到实体的组领导者。

stopped

将实体标记为从 rq 中删除并注定要终止。这是通过调用 drm_sched_entity_flush()drm_sched_fini() 设置的。

entity_idle

当实体未使用时发出信号,用于在 drm_sched_entity_fini() 中按顺序进行实体清理。

oldest_job_waiting

标记在软件队列中等待的最早作业

rb_tree_node

用于将此实体插入基于时间的优先级队列的节点

描述

实体将按照其对应的硬件环的顺序发出作业,并且调度器将根据调度策略在实体之间交替。

struct drm_sched_rq

要调度的实体队列。

定义:

struct drm_sched_rq {
    struct drm_gpu_scheduler        *sched;
    spinlock_t lock;
    struct drm_sched_entity         *current_entity;
    struct list_head                entities;
    struct rb_root_cached           rb_tree_root;
};

成员

sched

此 rq 所属的调度器。

保护 entitiesrb_tree_rootcurrent_entity

current_entity

要调度的实体。

entities

要调度的实体列表。

rb_tree_root

基于时间的 FIFO 调度实体优先级队列的根

描述

运行队列是一组实体,用于调度一个特定环的命令提交。它实现了调度策略,该策略选择要从中发出命令的下一个实体。

struct drm_sched_fence

与作业调度对应的栅栏。

定义:

struct drm_sched_fence {
    struct dma_fence                scheduled;
    struct dma_fence                finished;
    ktime_t deadline;
    struct dma_fence                *parent;
    struct drm_gpu_scheduler        *sched;
    spinlock_t lock;
    void *owner;
};

成员

scheduled

此栅栏将在作业被调度时由调度器发出信号。

finished

此栅栏将在作业完成时由调度器发出信号。

在为作业设置输出栅栏时,应使用此栅栏,因为它在 drm_sched_job_init() 中立即可用,并且驱动程序从 run_job() 返回的栅栏将在依赖关系解决之前不会创建。

deadline

drm_sched_fence.finished 上设置的截止时间,可能需要传播到 drm_sched_fence.parent

parent

在硬件上调度作业时,由 drm_sched_backend_ops.run_job 返回的栅栏。一旦父级发出信号,我们将发出 drm_sched_fence.finished 栅栏信号。

sched

作业所属的调度器实例,该实例具有此结构。

计划栅栏和已完成栅栏使用的锁。

owner

用于调试的作业所有者

struct drm_sched_job

要由实体运行的作业。

定义:

struct drm_sched_job {
    struct spsc_node                queue_node;
    struct list_head                list;
    struct drm_gpu_scheduler        *sched;
    struct drm_sched_fence          *s_fence;
    u32 credits;
    union {
        struct dma_fence_cb             finish_cb;
        struct work_struct              work;
    };
    uint64_t id;
    atomic_t karma;
    enum drm_sched_priority         s_priority;
    struct drm_sched_entity         *entity;
    struct dma_fence_cb             cb;
    struct xarray                   dependencies;
    unsigned long                   last_dependency;
    ktime_t submit_ts;
};

成员

queue_node

用于将此结构附加到实体中的作业队列。

list

作业参与“pending”和“done”列表。

sched

此作业正在或将要调度的调度器。由 drm_sched_job_arm() 设置。在 drm_sched_backend_ops.free_job() 完成之前有效。

s_fence

包含作业调度的栅栏。

credits

此作业贡献给调度器的信用额度数量

{unnamed_union}

匿名

finish_cb

已完成栅栏的回调。

work

帮助将作业终止重新调度到不同的上下文。

id

分配给调度器上调度的每个作业的唯一 ID。

karma

每次由该作业引起的挂起都会增加。如果此值超过调度器的挂起限制,则该作业将被标记为 guilty,并且不会进一步调度。

s_priority

作业的优先级。

entity

此作业所属的实体。

cb

s_fence 中父栅栏的回调。

dependencies

包含此作业的依赖项,形式为 struct dma_fence,请参阅 drm_sched_job_add_dependency()drm_sched_job_add_implicit_dependencies()

last_dependency

跟踪 dependencies 的信号

submit_ts

作业被推送到实体队列的时间。

描述

作业由驱动程序使用 drm_sched_job_init() 创建,并且一旦希望调度程序调度该作业,应调用 drm_sched_entity_push_job()

struct drm_sched_backend_ops

定义调度程序调用的后端操作

定义:

struct drm_sched_backend_ops {
    struct dma_fence *(*prepare_job)(struct drm_sched_job *sched_job, struct drm_sched_entity *s_entity);
    struct dma_fence *(*run_job)(struct drm_sched_job *sched_job);
    enum drm_gpu_sched_stat (*timedout_job)(struct drm_sched_job *sched_job);
    void (*free_job)(struct drm_sched_job *sched_job);
    u32 (*update_job_credits)(struct drm_sched_job *sched_job);
};

成员

prepare_job

当调度程序考虑接下来调度此作业时调用,以获取此作业要阻塞的另一个 struct dma_fence。一旦它返回 NULL,可能会调用 run_job()。

如果不需要对依赖项进行额外的准备,则可以为 NULL。当作业被杀死而不是运行时跳过。

run_job

一旦所有依赖项都已解决,则调用以执行作业。如果发生 timedout_job() 并且 drm_sched_job_recovery() 决定再次尝试,则可能会多次调用此函数。

timedout_job

当作业执行时间过长时调用,以触发 GPU 恢复。

此方法在工作队列上下文中调用。

驱动程序通常会发出重置以从 GPU 挂起中恢复,此过程通常遵循以下工作流程

  1. 使用 drm_sched_stop() 停止调度程序。这将暂停调度程序线程并取消超时工作,保证在我们重置硬件队列时不会排队任何内容

  2. 尝试优雅地停止非故障作业(可选)

  3. 发出 GPU 重置(驱动程序特定)

  4. 使用 drm_sched_resubmit_jobs() 重新提交作业

  5. 使用 drm_sched_start() 重新启动调度程序。此时,可以对新作业进行排队,并且调度程序线程将被解除阻塞

请注意,某些 GPU 具有不同的硬件队列,但需要全局重置 GPU,这需要在不同 drm_gpu_scheduler 的超时处理程序之间进行额外的同步。实现此同步的一种方法是在驱动程序级别创建一个有序工作队列(使用 alloc_ordered_workqueue()),并将此队列传递给 drm_sched_init(),以保证超时处理程序按顺序执行。在这种情况下,需要稍微调整上述工作流程

  1. 使用 drm_sched_stop() 停止受重置影响的所有调度程序

  2. 尝试优雅地停止所有受重置影响的队列上的非故障作业(可选)

  3. 在所有故障队列上发出 GPU 重置(驱动程序特定)

  4. 使用 drm_sched_resubmit_jobs() 重新提交所有受重置影响的调度程序上的作业

  5. 使用 drm_sched_start() 重新启动在步骤 #1 中停止的所有调度程序

当一切正常,并且底层驱动程序已启动或完成恢复时,返回 DRM_GPU_SCHED_STAT_NOMINAL。

如果设备不再可用,即已拔出,则返回 DRM_GPU_SCHED_STAT_ENODEV。

free_job

一旦作业的完成 fence 已发出信号并且需要清理时调用。

update_job_credits

当调度程序正在考虑执行此作业时调用。

此回调返回如果推送到硬件,作业将占用的信用额度。驱动程序可以使用此函数来动态更新作业的信用额度。例如,扣除已发出信号的本机 fence 的信用额度。

此回调是可选的。

描述

这些函数应在驱动程序端实现。

struct drm_gpu_scheduler

调度程序实例特定数据

定义:

struct drm_gpu_scheduler {
    const struct drm_sched_backend_ops      *ops;
    u32 credit_limit;
    atomic_t credit_count;
    long timeout;
    const char                      *name;
    u32 num_rqs;
    struct drm_sched_rq             **sched_rq;
    wait_queue_head_t job_scheduled;
    atomic64_t job_id_count;
    struct workqueue_struct         *submit_wq;
    struct workqueue_struct         *timeout_wq;
    struct work_struct              work_run_job;
    struct work_struct              work_free_job;
    struct delayed_work             work_tdr;
    struct list_head                pending_list;
    spinlock_t job_list_lock;
    int hang_limit;
    atomic_t *score;
    atomic_t _score;
    bool ready;
    bool free_guilty;
    bool pause_submit;
    bool own_submit_wq;
    struct device                   *dev;
};

成员

ops

驱动程序提供的后端操作。

credit_limit

此调度程序的信用额度

credit_count

此调度程序的当前信用计数

timeout

作业从调度程序中删除的时间。

name

正在使用此调度程序的环的名称。

num_rqs

运行队列的数量。这最多为 DRM_SCHED_PRIORITY_COUNT,因为每个优先级通常有一个运行队列,但可能会更少。

sched_rq

大小为 num_rqs 的运行队列的已分配数组;

job_scheduled

一旦调用 drm_sched_entity_do_release,调度程序将在此等待队列上等待,直到所有调度的作业完成。

job_id_count

用于为每个作业分配唯一 ID。

submit_wq

用于将 work_run_jobwork_free_job 排队的工作队列

timeout_wq

用于将 work_tdr 排队的工作队列

work_run_job

调用每个调度程序的 run_job 操作的工作。

work_free_job

调用每个调度程序的 free_job 操作的工作。

work_tdr

在超时时间过后,计划延迟调用 drm_sched_job_timedout

pending_list

当前位于作业队列中的作业列表。

job_list_lock

用于保护 pending_list 的锁。

hang_limit

一旦作业的挂起次数超过此限制,则将其标记为有罪,并且不再考虑进行调度。

score

帮助负载均衡器选择空闲调度程序的评分

_score

当驱动程序不提供评分时使用的评分

ready

标记底层硬件是否已准备好工作

free_guilty

超时处理程序的一个命中,用于释放有罪的作业。

pause_submit

暂停在 submit_wq 上排队 work_run_job

own_submit_wq

调度程序拥有 submit_wq 的分配

dev

系统 struct device

描述

为每个硬件环实现一个调度程序。

void drm_sched_tdr_queue_imm(struct drm_gpu_scheduler *sched)
  • 立即启动作业超时处理程序

参数

struct drm_gpu_scheduler *sched

应启动超时处理的调度程序。

描述

立即为指定的调度程序启动超时处理。

void drm_sched_fault(struct drm_gpu_scheduler *sched)

立即启动超时处理程序

参数

struct drm_gpu_scheduler *sched

应启动超时处理的调度程序。

描述

当驱动程序检测到硬件故障时,立即启动超时处理。

unsigned long drm_sched_suspend_timeout(struct drm_gpu_scheduler *sched)

暂停调度程序作业超时

参数

struct drm_gpu_scheduler *sched

要暂停超时的调度程序实例

描述

暂停调度程序的延迟工作超时。这是通过将延迟工作超时修改为任意大值来实现的,在本例中为 MAX_SCHEDULE_TIMEOUT。

返回剩余的超时时间

void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched, unsigned long remaining)

恢复调度程序作业超时

参数

struct drm_gpu_scheduler *sched

要恢复超时的调度程序实例

unsigned long remaining

剩余超时时间

描述

恢复调度程序的延迟工作超时。

void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad)

停止调度程序

参数

struct drm_gpu_scheduler *sched

调度程序实例

struct drm_sched_job *bad

导致超时的作业

描述

停止调度程序并删除并释放所有已完成的作业。此函数通常用于重置恢复(有关详细信息,请参阅 drm_sched_backend_ops.timedout_job() 的文档)。不要在调用 drm_sched_fini() 之前为调度程序拆解调用它。

注意

如果坏的任务可能在之后被使用,则不会被释放,因此,如果它不再是待处理列表的一部分,调用者有责任手动释放它。

void drm_sched_start(struct drm_gpu_scheduler *sched, int errno)

在重置后恢复任务

参数

struct drm_gpu_scheduler *sched

调度程序实例

int errno

要在待处理的栅栏上设置的错误

描述

此函数通常用于重置恢复(有关详细信息,请参阅 drm_sched_backend_ops.timedout_job() 的文档)。请勿在调度程序启动时调用它。调度程序本身在 drm_sched_init() 成功后即可完全运行。

void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched)

已弃用,请勿在新代码中使用!

参数

struct drm_gpu_scheduler *sched

调度程序实例

描述

重新提交任务是 AMD 提出的一种廉价的实现任务超时后恢复的方法。

事实证明这效果不是很好。首先,dma_fence 的实现和要求存在很多问题。要么实现存在与核心内存管理死锁的风险,要么违反了 dma_fence 对象的文档化实现细节。

驱动程序仍然可以保存和恢复其状态以进行恢复操作,但我们不应将其作为围绕 dma_fence 接口的通用调度程序功能。

int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, u32 credits, void *owner)

初始化一个调度程序任务

参数

struct drm_sched_job *job

要初始化的调度程序任务

struct drm_sched_entity *entity

要使用的调度程序实体

u32 credits

此任务对调度程序信用额度的贡献的信用数量

void *owner

用于调试的作业所有者

描述

有关锁定注意事项,请参阅 drm_sched_entity_push_job() 文档。

如果此函数成功返回,即使 **job** 在调用 drm_sched_job_arm() 之前中止,驱动程序也必须确保调用 drm_sched_job_cleanup()

请注意,此函数不会为 struct drm_sched_job 的每个结构成员分配有效值。请查看该结构的文档,了解谁在什么生命周期内设置哪个结构成员。

警告:amdgpu 滥用 drm_sched.ready 来指示硬件何时死机,这可能意味着 **entity** 没有有效的运行队列。在这种情况下,此函数返回 -ENOENT(可能应该返回 -EIO 作为更有意义的返回值)。

成功返回 0,否则返回负错误代码。

void drm_sched_job_arm(struct drm_sched_job *job)

准备一个调度程序任务以供执行

参数

struct drm_sched_job *job

要准备的调度程序任务

描述

这将准备一个调度程序任务以供执行。具体来说,它初始化 **job** 的 drm_sched_job.s_fence,以便可以将其附加到 struct dma_resv 或其他需要跟踪此任务完成情况的地方。

有关锁定注意事项,请参阅 drm_sched_entity_push_job() 文档。

只有在 drm_sched_job_init() 成功后才能调用此函数。

int drm_sched_job_add_dependency(struct drm_sched_job *job, struct dma_fence *fence)

将栅栏添加为任务依赖项

参数

struct drm_sched_job *job

要添加依赖项的调度程序任务

struct dma_fence *fence

要添加到依赖项列表中的 dma_fence。

描述

请注意,在成功和错误情况下都会消耗 **fence**。

返回

成功时为 0,或在扩展数组失败时返回错误。

int drm_sched_job_add_syncobj_dependency(struct drm_sched_job *job, struct drm_file *file, u32 handle, u32 point)

将 syncobj 的栅栏添加为任务依赖项

参数

struct drm_sched_job *job

要添加依赖项的调度程序任务

struct drm_file *file

drm 文件私有指针

u32 handle

要查找的 syncobj 句柄

u32 point

时间线点

描述

这将与给定 syncobj 匹配的栅栏添加到 **job**。

返回

成功时为 0,或在扩展数组失败时返回错误。

int drm_sched_job_add_resv_dependencies(struct drm_sched_job *job, struct dma_resv *resv, enum dma_resv_usage usage)

将 resv 中的所有栅栏添加到任务

参数

struct drm_sched_job *job

要添加依赖项的调度程序任务

struct dma_resv *resv

从中获取栅栏的 dma_resv 对象

enum dma_resv_usage usage

用于筛选栅栏的 dma_resv_usage

描述

这会将与给定用法匹配的所有栅栏从 **resv** 添加到 **job**。必须在持有 **resv** 锁的情况下调用。

返回

成功时为 0,或在扩展数组失败时返回错误。

int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, struct drm_gem_object *obj, bool write)

将隐式依赖项添加为任务依赖项

参数

struct drm_sched_job *job

要添加依赖项的调度程序任务

struct drm_gem_object *obj

从中添加新依赖项的 gem 对象。

bool write

任务是否可能写入对象(因此我们需要依赖于预留对象中的共享栅栏)。

描述

这应该在 drm_gem_lock_reservations() 在你的任务中使用的 GEM 对象数组之后,但在使用自己的栅栏更新预留之前调用。

返回

成功时为 0,或在扩展数组失败时返回错误。

void drm_sched_job_cleanup(struct drm_sched_job *job)

清理调度程序任务资源

参数

struct drm_sched_job *job

要清理的调度程序任务

描述

清理使用 drm_sched_job_init() 分配的资源。

如果 **job** 在调用 drm_sched_job_arm() 之前中止,驱动程序应从其错误回退代码中调用此函数。

过了那个无法返回的点后,**job** 将被提交给调度程序执行,并且应从 drm_sched_backend_ops.free_job 回调中调用此函数。

struct drm_gpu_scheduler *drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, unsigned int num_sched_list)

从 sched_list 中获取负载最小的 drm 调度器

参数

struct drm_gpu_scheduler **sched_list

drm_gpu_scheduler 的列表

unsigned int num_sched_list

sched_list 中 drm_gpu_scheduler 的数量

描述

返回负载最小的调度器的指针,如果所有 drm_gpu_scheduler 都未准备好,则返回 NULL

int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_backend_ops *ops, struct workqueue_struct *submit_wq, u32 num_rqs, u32 credit_limit, unsigned int hang_limit, long timeout, struct workqueue_struct *timeout_wq, atomic_t *score, const char *name, struct device *dev)

初始化一个 GPU 调度器实例

参数

struct drm_gpu_scheduler *sched

调度程序实例

const struct drm_sched_backend_ops *ops

此调度器的后端操作

struct workqueue_struct *submit_wq

用于提交的工作队列。如果为 NULL,则会分配并使用一个有序的 wq

u32 num_rqs

运行队列的数量,每个优先级一个,最多为 DRM_SCHED_PRIORITY_COUNT

u32 credit_limit

此调度器可以从所有作业中持有的信用额度

unsigned int hang_limit

在放弃作业之前允许作业挂起的次数

long timeout

调度器的超时值(以 jiffies 为单位)

struct workqueue_struct *timeout_wq

用于超时工作的工作队列。如果为 NULL,则使用 system_wq

atomic_t *score

与其它调度器共享的可选分数原子变量

const char *name

用于调试的名称

struct device *dev

目标 struct device

描述

成功返回 0,否则返回错误代码。

void drm_sched_fini(struct drm_gpu_scheduler *sched)

销毁一个 GPU 调度器

参数

struct drm_gpu_scheduler *sched

调度程序实例

描述

拆卸并清理调度器。

这将停止通过 drm_sched_backend_ops.run_job() 向硬件提交新作业。 因此,不会为仍在 drm_gpu_scheduler.pending_list 中的所有作业调用 drm_sched_backend_ops.free_job()。 目前没有解决此问题的方案。 因此,驱动程序需要确保

  1. drm_sched_fini() 仅在所有提交的作业都调用了 drm_sched_backend_ops.free_job() 后调用,或者

  2. 在运行 drm_sched_fini() 后,未调用 drm_sched_backend_ops.free_job() 的作业被手动释放。

FIXME:解决上述问题,并防止此函数在任何情况下泄漏 drm_gpu_scheduler.pending_list 中的作业。

void drm_sched_increase_karma(struct drm_sched_job *bad)

更新 sched_entity 的 guilty 标志

参数

struct drm_sched_job *bad

导致超时的作业

描述

在 ‘bad’ 作业导致的每次挂起时递增。 如果此值超过调度器的挂起限制,则相应的调度实体会被标记为 guilty,并且不会再调度来自该实体的作业

bool drm_sched_wqueue_ready(struct drm_gpu_scheduler *sched)

调度器是否准备好提交

参数

struct drm_gpu_scheduler *sched

调度程序实例

描述

如果准备好提交,则返回 true

void drm_sched_wqueue_stop(struct drm_gpu_scheduler *sched)

停止调度器提交

参数

struct drm_gpu_scheduler *sched

调度程序实例

描述

阻止调度器从实体中提取新作业。它还会停止通过 drm_sched_backend_ops.free_job() 自动释放作业。

void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched)

启动调度器提交

参数

struct drm_gpu_scheduler *sched

调度程序实例

描述

drm_sched_wqueue_stop() 停止调度器后重新启动调度器。

此函数对于“传统”启动不是必需的。 在 drm_sched_init() 成功后,调度器即可完全运行。

int drm_sched_entity_init(struct drm_sched_entity *entity, enum drm_sched_priority priority, struct drm_gpu_scheduler **sched_list, unsigned int num_sched_list, atomic_t *guilty)

初始化一个上下文实体,该实体在提交到 HW 环时由调度器使用。

参数

struct drm_sched_entity *entity

要初始化的调度器实体

enum drm_sched_priority priority

实体的优先级

struct drm_gpu_scheduler **sched_list

可以提交此实体作业的 drm 调度器列表

unsigned int num_sched_list

sched_list 中 drm 调度器的数量

atomic_t *guilty

当发现此队列上的作业导致超时时,atomic_t 设置为 1

描述

请注意,sched_list 必须至少有一个元素才能调度实体。

有关稍后在运行时更改 **priority**,请参阅 drm_sched_entity_set_priority()。 有关在运行时更改调度器集 **sched_list**,请参阅 drm_sched_entity_modify_sched()

通过调用 drm_sched_entity_fini() 来清理实体。 另请参阅 drm_sched_entity_destroy()

成功时返回 0,失败时返回负错误代码。

void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, struct drm_gpu_scheduler **sched_list, unsigned int num_sched_list)

修改实体的调度器。

参数

struct drm_sched_entity *entity

要初始化的调度器实体

struct drm_gpu_scheduler **sched_list

新的 DRM 调度器列表,将替换现有的 entity->sched_list。

unsigned int num_sched_list

sched_list 中 drm 调度器的数量

描述

请注意,此函数必须在与 drm_sched_job_arm()drm_sched_entity_push_job() 相同的通用锁下调用 **entity**,或者驱动程序需要通过其他方式保证在向 **entity** 推送新作业时永远不会调用此函数。

int drm_sched_entity_error(struct drm_sched_entity *entity)

返回上次调度作业的错误。

参数

struct drm_sched_entity *entity

要检查的调度器实体。

描述

选择性地返回上次调度作业的错误。当新作业被推送到硬件时,结果可能随时更改。

long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)

刷新上下文实体。

参数

struct drm_sched_entity *entity

调度器实体。

long timeout

以节拍为单位等待队列变空的时间。

描述

drm_sched_entity_fini() 分割成两个函数,第一个函数执行等待,从运行队列中删除实体,并在进程被杀死时返回错误。

返回输入超时剩余的节拍时间。

void drm_sched_entity_fini(struct drm_sched_entity *entity)

销毁上下文实体。

参数

struct drm_sched_entity *entity

调度器实体。

描述

清理由 drm_sched_entity_init() 初始化的 **entity**。

如果可能仍有正在运行或新加入队列的作业,则必须首先调用 drm_sched_entity_flush()。然后此函数会遍历实体,并在进程被杀死时使用错误代码向所有作业发出信号。

void drm_sched_entity_destroy(struct drm_sched_entity *entity)

销毁上下文实体。

参数

struct drm_sched_entity *entity

调度器实体。

描述

为了方便起见,调用 drm_sched_entity_flush()drm_sched_entity_fini() 作为便捷的包装器。

void drm_sched_entity_set_priority(struct drm_sched_entity *entity, enum drm_sched_priority priority)

设置实体的优先级。

参数

struct drm_sched_entity *entity

调度器实体。

enum drm_sched_priority priority

调度器优先级。

描述

更新用于实体的运行队列的优先级。

void drm_sched_entity_push_job(struct drm_sched_job *sched_job)

将作业提交到实体的作业队列。

参数

struct drm_sched_job *sched_job

要提交的作业。

注意

为了保证插入队列的顺序与作业的栅栏序列号相匹配,此函数应在为 struct drm_sched_entity 设置的公共锁下,与 drm_sched_job_arm() 一起调用,该实体在 drm_sched_job_init() 中为 **sched_job** 设置。