DRM 内存管理

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

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

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

转换表管理器 (TTM)

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

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

待办事项:在此处添加更多设计背景和信息。

enum ttm_caching

CPU 缓存和 BUS 侦听行为。

常量

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;
    struct dmem_cgroup_region *cg;
};

成员

use_type

已启用内存类型。

use_tt

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

bdev

此管理器所属的 TTM 设备

size

托管区域的大小。

func

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

move_lock

移动栅栏锁

move

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

lru

此内存类型的 lru 列表。

usage

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

cg

用于内存核算的 dmem_cgroup_region,如果不是 NULL。

描述

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

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 dmem_cgroup_pool_state *css;
    struct ttm_lru_item lru;
};

成员

start

分配的开始。

size

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

mem_type

分配的资源类型。

placement

放置标志。

bus

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

bo

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

css

此资源所记入的 cgroup 状态

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

批量移动范围中的第一个 res

last

批量移动范围中的最后一个 res

描述

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

struct io_mapping 表示底层线性 io_memory。

st

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

start

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

cache

用于快速查找的 Scatterlist 遍历缓存。

cache.sg

指向当前缓存的 scatterlist 段的指针。

cache.i

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

cache.end

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

cache.offs

sgiomap 的第一个偏移量。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 (cursor, res)

迭代所有资源

参数

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_BACKED_UP           BIT(5);
#define TTM_TT_FLAG_PRIV_POPULATED      BIT(6);
    uint32_t page_flags;
    uint32_t num_pages;
    struct sg_table *sg;
    dma_addr_t *dma_address;
    struct file *swap_storage;
    struct file *backup;
    enum ttm_caching caching;
    struct ttm_pool_tt_restore *restore;
};

成员

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 直接映射这些页面。

请注意,enum 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_BACKED_UP:仅限 TTM 内部使用。 如果 struct ttm_tt 已(可能部分)备份,则设置此标志。

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

num_pages

页面数组中的页数。

sg

用于通过 dma-buf 的 SG 对象。

dma_address

页面的 DMA(总线)地址。

swap_storage

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

backup

指向备份的 tt 的备份结构的指针。 可以与 swap_storage 统一。 同时,驱动程序的 ttm_tt_create() 回调负责分配此字段。

caching

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

restore

从备份状态部分还原。 TTM 私有

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

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

bool ttm_tt_is_swapped(const struct ttm_tt *tt)

ttm_tt 是否已换出或备份

参数

const struct ttm_tt *tt

struct ttm_tt

返回值

如果已交换或备份,则为 true,否则为 false。

bool ttm_tt_is_backed_up(const struct ttm_tt *tt)

ttm_tt 是否已备份

参数

const struct ttm_tt *tt

struct ttm_tt

返回值

如果已交换或备份,则为 true,否则为 false。

void ttm_tt_clear_backed_up(struct ttm_tt *tt)

清除 ttm_tt 备份状态

参数

struct ttm_tt *tt

struct ttm_tt

描述

驱动程序可以使用此函数来清除备份状态,例如,在销毁或重新验证已清除的 tt 之前。

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_backup_flags

用于控制备份行为的标志。

定义:

struct ttm_backup_flags {
    u32 purge : 1;
    u32 writeback : 1;
};

成员

purge

释放页面而不备份。 绕过池。

writeback

尝试将内容直接复制到交换空间,即使这意味着阻塞对外部内存的写入。

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 的指针。

int ttm_tt_setup_backup(struct ttm_tt *tt)

为 ttm_tt 分配并分配备份结构

参数

struct ttm_tt *tt

为其分配和分配备份结构的 ttm_tt。

描述

分配一个备份结构以用于 tt 备份。 这通常应该在 bo 创建时完成,以避免在收缩时进行分配。

返回值

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

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

order

我们的页面具有的分配顺序

caching

我们的页面具有的缓存类型

shrinker_list

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

lock

页面列表的保护

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 对象,因此创建一个嵌入 struct struct drm_gem_object 实例的特定于驱动程序的 GEM 对象结构类型。

要创建 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() 来初始化 GEM 对象(称为私有 GEM 对象),从而创建没有 shmfs 支持的 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 对象,驱动程序必须确保它们删除它们拥有的引用(例如在对象创建时获取的初始引用),而无需对句柄进行任何特殊考虑。例如,在哑创建操作的实现中,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 offset 参数。

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 处理程序。

故障操作处理程序负责在发生页面错误时将各个页面映射到用户空间。根据内存分配方案,驱动程序可以在故障时分配页面,也可以决定在创建对象时为 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 内存映射支持

内存一致性

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

命令执行

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

GEM 函数参考

enum drm_gem_object_status

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

常量

DRM_GEM_OBJECT_RESIDENT

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

DRM_GEM_OBJECT_PURGEABLE

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

DRM_GEM_OBJECT_ACTIVE

对象当前正在活动提交中使用

描述

用于 fdinfo 内存统计的状态位掩码,请参见 drm_gem_object_funcs.statusdrm_show_fdinfo()。请注意,对象可以报告 DRM_GEM_OBJECT_PURGEABLE 并且处于活动状态或未驻留状态,在这种情况下,drm_show_fdinfo() 不会将其计为可清除的。因此,驱动程序无需检查缓冲区是否空闲和驻留即可返回此位,即使用户空间可以在缓冲区仍在 GPU 上繁忙时将其标记为可清除。在它变为空闲之前,它不会在 puregeable 统计信息中报告。状态 gem object 函数不需要考虑这一点。

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

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

另请参见 drm_prime_pages_to_sg()

vmap

返回缓冲区的虚拟地址。drm_gem_dmabuf_vmap() 帮助程序使用。使用持有的 GEM 预留锁调用。

此回调是可选的。

vunmap

释放先前由 vmap 返回的地址。drm_gem_dmabuf_vunmap() 帮助程序使用。使用持有的 GEM 预留锁调用。

此回调是可选的。

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。使用持有的 GEM 预留锁调用。

此回调是可选的。

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;
};

成员

lock

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

count

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

list

LRU 列表。

描述

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

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 dev 此对象所属。

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

A 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 对象是否共享。

bool drm_gem_is_imported(const struct drm_gem_object *obj)

测试是否已导入 GEM 对象的缓冲区

参数

const struct drm_gem_object *obj

GEM 对象

返回值

如果已导入 GEM 对象的缓冲区,则为 True,否则为 false

drm_gem_gpuva_set_lock

drm_gem_gpuva_set_lock (obj, lock)

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

参数

obj

drm_gem_object

lock

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

描述

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

void drm_gem_gpuva_init(struct drm_gem_object *obj)

初始化 GEM 对象的 gpuva 列表

参数

struct drm_gem_object *obj

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

要删除的用户空间句柄

描述

从已使用drm_gem_handle_create()添加的filp查找表中删除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偏移量的返回位置

描述

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

返回值

成功时返回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()。如果您有特殊的区域约束,请在通过mapping_set_gfp_mask()调用drm_gem_object_init()之后设置它们。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后同步映射缓冲区内存。

此函数主要用于实现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 map 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_objects

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))

实现 shrinker.scan_objects 的助手

参数

struct drm_gem_lru *lru

要扫描的 LRU

unsigned int nr_to_scan

要尝试回收的页数

unsigned long *remaining

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

bool (*shrink)(struct drm_gem_object *obj)

尝试缩小/回收对象的 回调。

描述

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

如果 LRU 可能包含活动缓冲区,则 shrink 回调有责任检查这一点(即 dma_resv_test_signaled()),或者如果需要,则阻塞直到缓冲区空闲。

int drm_gem_evict_locked(struct drm_gem_object *obj)

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

参数

struct drm_gem_object *obj

有问题的 obj

GEM DMA 助手函数参考

DRM GEM/DMA 助手是一种提供呈现给设备的缓冲区对象的方法,这些对象是作为连续的内存块呈现的。这对于不支持 scatter-gather DMA(直接或通过使用紧密连接的 IOMMU)的设备很有用。

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

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

对于 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 缓冲区的 scatter/gather 表。该表可以有多个条目,但保证具有连续的 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 处理程序。

返回值

指向已固定页面的 scatter/gather 表的指针,如果失败,则为 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 操作。

此宏是 DRM_GEM_DMA_DRIVER_OPS 的变体,适用于覆盖 struct rm_driver.dumb_create 的默认实现的驱动程序。如果可能,请使用 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 操作。

此宏是 DRM_GEM_DMA_DRIVER_OPS_VMAP 的变体,适用于覆盖 struct drm_driver.dumb_create 的默认实现的驱动程序。如果可能,请使用 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 平台中用于为给定缓冲区提出地址映射。它旨在用作 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 对象提供已固定页面的 scatter/gather 表

参数

struct drm_gem_dma_object *dma_obj

DMA GEM 对象

描述

此函数通过调用标准 DMA 映射 API 导出 scatter/gather 表。

返回值

指向已固定页面的 scatter/gather 表的指针,如果失败,则为 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)

从另一个驱动程序的已固定页面的 scatter/gather 表生成 DMA GEM 对象

参数

struct drm_device *dev

要导入到的设备

struct dma_buf_attachment *attach

DMA-BUF 附件

struct sg_table *sgt

已固定页面的 scatter/gather 表

描述

此函数导入由另一个驱动程序通过 DMA-BUF 导出的 scatter/gather 表。导入的缓冲区必须在物理上在内存中连续(即,scatter/gather 表必须包含单个条目)。使用 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 导入另一个驱动程序的 scatter/gather 表并获取缓冲区的虚拟地址

参数

struct drm_device *dev

DRM 设备

struct dma_buf_attachment *attach

DMA-BUF 附件

struct sg_table *sgt

已固定页面的 Scatter/gather 表

描述

此函数使用 drm_gem_dma_prime_import_sg_table() 导入 scatter/gather 表,并使用 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 对象上操作的函数接收 drm_gem_shmem_object 结构体。对于 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;
    refcount_t pages_use_count;
    refcount_t pages_pin_count;
    int madv;
    struct list_head madv_list;
    struct sg_table *sgt;
    void *vaddr;
    refcount_t 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

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

pages_pin_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

已固定页面的 Scatter/gather 表

描述

此函数导入由另一个驱动程序通过 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 辅助库。 每个活动缓冲区对象都存储在视频 RAM 中。 活动缓冲区用于绘制当前帧,通常类似于帧的扫描输出缓冲区或光标图像。 如果 VRAM 中没有更多空间,则可以将非活动的 GEM 对象移动到系统内存。

要初始化 VRAM 辅助库,请调用 drmm_vram_helper_init()。该函数在 struct drm_device.vram_mm 中分配并初始化一个 struct drm_vram_mm 实例。 使用 DRM_GEM_VRAM_DRIVER 初始化 struct drm_driverDRM_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 管理视频 RAM 区域,并向用户空间提供 GEM VRAM 对象。

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

对于绘图或扫描输出操作,分别需要将缓冲区对象固定在视频 RAM 中。 调用带有 DRM_GEM_VRAM_PL_FLAG_VRAMDRM_GEM_VRAM_PL_FLAG_SYSTEMdrm_gem_vram_pin(),以将缓冲区对象固定在视频 RAM 或系统内存中。 调用 drm_gem_vram_unpin() 以在之后释放固定的对象。

固定在视频 RAM 中的缓冲区对象在该内存区域中具有固定地址。 调用 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 对象对固定和映射操作执行引用计数。 因此,使用 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,则缓冲区将固定在其当前位置(视频 RAM 或系统内存)。

小的缓冲区对象(例如光标图像)如果固定在视频 RAM 的中间,可能会导致内存碎片。在只有少量视频 RAM 的设备上,这尤其是一个问题。碎片化可能会阻止主帧缓冲区适应,即使总体上有足够的内存。修饰符 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_dumbstruct 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 的参数

描述

此函数要求驱动程序使用 drm_device.vram_mm 作为其 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 的新实例并将其存储在 struct 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

要测试的模式

描述

此函数测试是否有足够的视频内存可用于指定的显示模式。Atomic 模式设置需要在逐出活动帧缓冲区之前将指定的帧缓冲区导入到视频内存中。因此,任何帧缓冲区最多可能会消耗可用 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)

打印 debugfs 的 ttm_buffer_object 信息

参数

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)

实现结构 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() 为 dumb-buffer 实现返回该偏移量。

参见 struct drm_driver.dumb_map_offset

返回值

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

VMA 偏移管理器

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

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

您不得在单个 address_space 上使用多个偏移管理器。否则,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 函数调用。在通过 drm_vma_offset_unlock_lookup() 释放锁之前,所有其他上下文都被阻止访问 VMA。

如果您需要引用 drm_vma_offset_lookup_locked() 返回的对象,然后再再次释放此锁,请使用此选项。

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

注意

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

void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)

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

参数

struct drm_vma_offset_manager *mgr

管理器对象

描述

释放 lookup-lock。有关更多信息,请参见 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)

返回用户空间 mmap 的清理偏移量

参数

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)

初始化新的 offset-manager

参数

struct drm_vma_offset_manager *mgr

管理器对象

unsigned long page_offset

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

unsigned long size

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

描述

初始化新的 offset-manager。可用于管理器的偏移量和区域大小作为 page_offsetsize 给出。两者均解释为页码,而不是字节。

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

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 已在此列表中,则增加 ref-count。

允许用户列表在 drm_vma_offset_add()drm_vma_offset_remove() 调用之间保留。即使节点当前未添加到任何偏移管理器,也可以调用它。

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

这在内部是针对并发访问锁定的。

返回值

成功时为 0,内部故障时为负错误代码 (out-of-mem)

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() 不同,它不是 ref-counted,因此 drm_vma_node_revoke() 应该仅在此之后调用一次。

这在内部是针对并发访问锁定的。

返回值

成功时为 0,内部故障时为负错误代码 (out-of-mem)

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 的 ref-count。如果 ref-count 降至零,则从列表中删除 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 中的跨设备缓冲区共享框架,最初是为多 GPU 平台的 OPTIMUS 系列创建的。对于用户空间 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。它在 PRIME_HANDLE_TO_FD IOCTL 中获取此引用,当它首次调用 drm_gem_object_funcs.export 并将导出的 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 的替代品,那么它将收到一个 fd->handle 请求,用于它创建的 GEM 对象。驱动程序应检测到这种情况,并从 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 attach 和 detach 时间处理。后备存储本身由 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 {
};

成员

描述

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

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(),以便通用 GEM 驱动程序可以使用它,这些驱动程序使用 drm_gem_dmabuf_release()。除了调用 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 的标志

描述

这是 GEM 驱动程序的 drm_gem_object_funcs.export 函数的实现,使用 PRIME 辅助函数。它在 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-table

描述

这是 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 抖动是一个相当陡峭的悬崖,而不是一个真正的问题。

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。

返回值

成功时为 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。

返回值

成功时为 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 的范围,则迭代器可能会遍历特殊的 _unallocated_ 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 插入到分配器中,这意味着 start、size 和 color 必须由调用者设置。所有其他字段必须清除为 0。这对于使用必须在范围分配器设置之前设置的预分配对象初始化分配器很有用,例如,当接管固件帧缓冲区时。

返回值

成功时返回 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

微调分配搜索和放置

描述

预分配的 node 必须清除为 0。

返回值

成功时为 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 清除为 0。

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 signalling 关键部分插入 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,但它也可以是“dummy”对象,可以使用 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_exec 循环,同时使用诸如 drm_gpuvm_prepare_range()drm_gpuvm_prepare_objects() 等辅助函数来锁定额外的 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_for_each_op() 迭代的 drm_gpuva_op 列表。此列表包含 drm_gpuva_ops,类似于在调用 drm_gpuvm_sm_map()drm_gpuvm_sm_unmap() 时收到的回调。虽然这种方式需要更多内存(以分配 drm_gpuva_ops),但它为驱动程序提供了一种多次迭代 drm_gpuva_op 的方法,例如,一次在可能进行内存分配的上下文中(例如,以分配 GPU 页表),另一次在 dma-fence signalling 关键路径中。

为了更新 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_object 的后续调用 drm_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,则禁用内部锁定并启用相应的 lockdep 检查。这是对能够获取相应的 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

list_head 用于将此对象附加到 drm_gpuvm_bo

rb

包含数据的结构,用于在 rb 树中存储 drm_gpuvas

rb.node

rb 树节点

rb.entry

list_head 用于另外连接 drm_gpuvas,其顺序与它们在间隔树中出现的顺序相同。这有助于从通过 rb 树找到的起始节点保持迭代 drm_gpuvas,同时对 rb 树本身进行修改。

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 树

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

list_head 存储用作外部对象的 drm_gpuvm_bos

extobj.local_list

指向本地列表的指针,该列表临时存储来自外部对象列表的条目

extobj.lock

用于保护 extobj 列表的自旋锁

evict

保存逐出列表和逐出列表锁的结构

evict.list

list_head 存储当前正在逐出的 drm_gpuvm_bos

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_object dma_resvdrm_gpuvms dma_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 将在此之前开始(但可能会重叠)

描述

此迭代器遍历 drm_gpuvm 中位于 start__end__ 之间的所有 drm_gpuvas。它的实现类似于 list_for_each(),但使用 drm_gpuvm 的内部间隔树来加速对起始 drm_gpuva 的搜索,因此对于元素的删除是不安全的。它假定 end__drm_gpuvm 中(或是在 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 将在此之前开始(但可能会重叠)

描述

此迭代器遍历 drm_gpuvm 中位于 start__end__ 之间的所有 drm_gpuvas。它的实现类似于 list_for_each_safe(),但使用 drm_gpuvm 的内部间隔树来加速对起始 drm_gpuva 的搜索,因此对于元素的删除是安全的。它假定 end__drm_gpuvm 中(或是在 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_execdrm_gpuvm 抽象

定义:

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 预留的栅栏数

extra

回调和相应的私有数据,供驱动程序锁定任意其他 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 的实例,并在 GEM 对象在此 GPU-VM 中的最后一次映射被取消映射时释放该实例。

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

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_gpuva 所关联的 drm_gpuvm_bo

描述

此迭代器遍历与 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_gpuva 所关联的 drm_gpuvm_bo

描述

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

调用方必须持有 GEM 的 gpuva 锁。

enum drm_gpuva_op_type

GPU VA 操作类型

常量

DRM_GPUVA_OP_MAP

map 操作类型

DRM_GPUVA_OP_REMAP

remap 操作类型

DRM_GPUVA_OP_UNMAP

unmap 操作类型

DRM_GPUVA_OP_PREFETCH

prefetch 操作类型

DRM_GPUVA_OP_DRIVER

驱动程序定义的操作类型

描述

用于更改 drm_gpuvm 跟踪的 GPU VA 映射的操作。

struct drm_gpuva_op_map

GPU VA map 操作

定义:

struct drm_gpuva_op_map {
    struct {
        u64 addr;
        u64 range;
    } va;
    struct {
        u64 offset;
        struct drm_gem_object *obj;
    } gem;
};

成员

va

包含 map 操作的地址和范围的结构

va.addr

新映射的基址

va.range

新映射的范围

gem

包含 drm_gem_object 及其偏移的结构

gem.offset

drm_gem_object 中的偏移

gem.obj

要映射的 drm_gem_object

描述

此结构表示 DRM GPU VA 管理器生成的单个 map 操作。

struct drm_gpuva_op_unmap

GPU VA unmap 操作

定义:

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 管理器生成的单个 unmap 操作。

struct drm_gpuva_op_remap

GPU VA remap 操作

定义:

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

原始现有映射的 unmap 操作

描述

这表示 DRM GPU VA 管理器生成的单个 remap 操作。

当通过插入新的 GPU VA 映射或通过部分取消映射现有映射来拆分现有 GPU VA 映射时,会生成 remap 操作,因此它最多由两个 map 和一个 unmap 操作组成。

unmap 操作负责删除原始现有映射。prev 用于重新映射前一部分,next 用于重新映射后续部分。

如果新映射的起始地址与旧映射的起始地址对齐,或者新映射的结束地址与旧映射的结束地址对齐,则 prevnext 为 NULL。

请注意,使用专用 remap 操作而不是任意 unmap 和 map 操作的原因是让驱动程序有机会从 unmap 操作的 drm_gpuva 结构(通常嵌入在更大的驱动程序特定结构中)中提取驱动程序特定数据来创建新映射。

struct drm_gpuva_op_prefetch

GPU VA prefetch 操作

定义:

struct drm_gpuva_op_prefetch {
    struct drm_gpuva *va;
};

成员

va

要预取的 drm_gpuva

描述

此结构表示 DRM GPU VA 管理器生成的单个 prefetch 操作。

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

list_head 用于在 drm_gpuva_ops 中分发此结构的实例。

op

操作的类型

{unnamed_union}

anonymous

map

map 操作

remap

remap 操作

unmap

unmap 操作

prefetch

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 使用的回调,以向驱动程序提供 map 和 unmap 操作的拆分/合并步骤。

void drm_gpuva_op_remap_to_unmap_range(const struct drm_gpuva_op_remap *op, u64 *start_addr, u64 *range)

帮助程序获取 remap 操作的 unmap 阶段的起始地址和范围。

参数

const struct drm_gpuva_op_remap *op

Remap 操作。

u64 *start_addr

所需 unmap 的起点的输出指针。

u64 *range

所需 unmap 的长度的输出指针。

描述

给定的起始地址和范围将被设置为表示先前被重新映射的映射覆盖但现在为空的地址空间范围。

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

resv drm_gem_object,提供 GPUVM 的通用 dma_resv

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_gpuvm 的 extobj 列表中,并且相应的 drm_gem_object 实际上是一个外部对象,则将其添加到该列表中。

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

用于初始化 prevnextdrm_gpuva_op_remap

描述

移除当前映射的 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()。

回调序列可以包含映射、取消映射和重新映射操作,但如果不需要任何操作,则回调序列也可能为空,例如,如果请求的映射已经以完全相同的方式存在。

可以有任意数量的取消映射操作,最多两个重新映射操作和一个映射操作。后者表示调用者请求的原始映射操作。

返回值

成功时为 0,或者为负错误代码

int drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, void *priv, u64 req_addr, u64 req_range)

创建 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()。

回调序列可以包含取消映射和重新映射操作,具体取决于是否存在要拆分的实际重叠映射。

可以有任意数量的取消映射操作和最多两个重新映射操作。

返回值

成功时为 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 进行迭代,并且必须按给定的顺序进行处理。它可以包含映射、取消映射和重新映射操作,但如果不需要任何操作,则它也可能为空,例如,如果请求的映射已经以完全相同的方式存在。

可以有任意数量的取消映射操作,最多两个重新映射操作和一个映射操作。后者表示调用者请求的原始映射操作。

请注意,在再次使用另一个映射请求调用此函数之前,必须更新 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)

创建 drm_gpuva_ops 以拆分取消映射

参数

struct drm_gpuvm *gpuvm

表示 GPU VA 空间的 drm_gpuvm

u64 req_addr

要取消映射的范围的起始地址

u64 req_range

要取消映射的映射的范围

描述

此函数创建要执行的操作列表,以取消映射和(如果需要)拆分与取消映射范围重叠的映射。

该列表可以使用 drm_gpuva_for_each_op 进行迭代,并且必须按给定的顺序进行处理。它可以包含取消映射和重新映射操作,具体取决于是否存在要拆分的实际重叠映射。

可以有任意数量的取消映射操作和最多两个重新映射操作。

请注意,在再次使用另一个要取消映射的范围调用此函数之前,必须更新 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

要预取的映射范围

描述

此函数创建要执行预取的 operations 列表。

可以使用 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 抽象

描述

此函数创建一个 operations 列表,用于为连接到 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 结构,包括所有与其关联的 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 管理器

描述

清理内存管理器资源和 freelist

struct drm_buddy_block *drm_get_buddy(struct drm_buddy_block *block)

获取 buddy 地址

参数

struct drm_buddy_block *block

DRM buddy 块

描述

返回 block 的相应 buddy 块,如果这是一个根块并且无法进一步合并,则返回 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

输入 list head 以释放块

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

已分配块的输入和输出列表。 MUST 包含单个块作为输入以进行修剪。 成功后,将包含构成 new_size 的新分配块。 块总是按升序排列

描述

对于连续分配,我们将大小向上舍入为最接近的 2 的幂值,驱动程序使用 actual 大小,因此剩余部分未使用,并且可以选择使用此函数释放

返回值

成功时为 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 buddy 管理器

u64 start

此块允许范围的开始

u64 end

此块允许范围的结束

u64 size

分配大小(以字节为单位)

u64 min_block_size

分配对齐方式

struct list_head *blocks

添加已分配块的输出 list head

unsigned long flags

DRM_BUDDY_*_ALLOCATION 标志

描述

在范围限制上调用的 alloc_range_bias(),它遍历树并返回所需的块。

没有强制范围限制时调用的 alloc_from_freelist(),它从 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)

刷新一组页面的 dcache 行。

参数

struct page *pages[]

要刷新的页面列表。

unsigned long num_pages

数组中的页数。

描述

刷新指向数组中页面地址的每个数据缓存行条目。

void drm_clflush_sg(struct sg_table *st)

刷新指向散点/聚集的 dcache 行。

参数

struct sg_table *st

struct sg_table。

描述

刷新指向 sg 中地址的每个数据缓存行条目。

void drm_clflush_virt_range(void *addr, unsigned long length)

刷新区域的 dcache 行

参数

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 区域读取,如果不存在此类 beast,则回退到普通 memcpy。

DRM 同步对象

DRM 同步对象(syncobj,参见 struct drm_syncobj)为同步原语提供了一个容器,用户空间可以使用该原语显式同步 GPU 命令,可以在用户空间进程之间共享,并且可以在不同的 DRM 驱动程序之间共享。 它们的主要用例是实现 Vulkan 栅栏和信号量。 syncobj 用户空间 API 提供了用于以下几种操作的 ioctl

  • 创建和销毁 syncobj

  • 将 syncobj 导入/导出到 syncobj 文件描述符/从 syncobj 文件描述符导出

  • 将 syncobj 的底层栅栏导入/导出到同步文件/从同步文件导出

  • 重置 syncobj(将其栅栏设置为 NULL)

  • 发出 syncobj 信号(设置一个平凡的发出信号的栅栏)

  • 等待 syncobj 的栅栏出现并发出信号

syncobj 用户空间 API 还提供了操作,用于根据 struct dma_fence_chain 的时间线而不是单个 struct dma_fence 操作 syncobj,通过以下操作

  • 发出时间线上给定点的信号

  • 等待给定点出现和/或发出信号

  • 从/向时间线的给定点导入和导出

在其核心,syncobj 只是一个 struct dma_fence 指针的包装器,该指针可能为 NULL。 首次创建 syncobj 时,其指针为 NULL 或指向已发出信号的栅栏的指针,具体取决于 DRM_SYNCOBJ_CREATE_SIGNALED 标志是否传递给 DRM_IOCTL_SYNCOBJ_CREATE

如果 syncobj 被视为二进制原语(其状态为发出信号或未发出信号),则当在 DRM 驱动程序中排队 GPU 工作以发出 syncobj 信号时,syncobj 的栅栏将被完成该工作发出信号的栅栏替换。 如果 syncobj 被视为时间线原语,则当在 DRM 驱动程序中排队 GPU 工作以发出 syncobj 的给定点信号时,则会创建一个新的 struct dma_fence_chain,该链指向 DRM 驱动程序的栅栏,也指向 syncobj 中的前一个栅栏。 新的 struct dma_fence_chain 栅栏替换 syncobj 的栅栏,并且将通过 DRM 驱动程序的工作的完成以及与先前位于 syncobj 中的栅栏相关的任何工作发出信号。

当在 DRM 驱动程序中排队 GPU 工作等待 syncobj 时,在排队工作时,它在将工作提交到硬件之前等待 syncobj 的栅栏。 该栅栏是

  • 如果 syncobj 被视为二进制原语,则为 syncobj 的当前栅栏。

  • 如果 syncobj 被视为时间线原语,则为与给定点关联的 struct dma_fence

如果 syncobj 的栅栏为 NULL 或不在 syncobj 的时间线中,则预期排队操作将失败。

使用二进制 syncobj,对 syncobj 的栅栏的所有操作都按照在用户空间调用 ioctl 时的当前栅栏进行,而不管该操作是立即主机端操作(发出信号或重置)还是在某些驱动程序队列中排队的操作。 DRM_IOCTL_SYNCOBJ_RESETDRM_IOCTL_SYNCOBJ_SIGNAL 可用于通过将其指针重置为 NULL 或将其指针设置为已发出信号的栅栏来从主机操作 syncobj。

使用时间线 syncobj,synobj 的栅栏的所有操作都按照引用时间线上点的 u64 值进行。 有关如何在时间线中找到给定点,请参见 dma_fence_chain_find_seqno()

请注意,在处理视为时间线的 syncobj 时,应用程序应注意始终使用时间线集 ioctl()。 将二进制集 ioctl() 与视为时间线的 syncobj 一起使用可能会导致不正确的同步。 通过使用 0 的点值,通过时间线集 ioctl() 支持使用二进制 syncobj,这将重现二进制集 ioctl() 的行为(例如,在发出信号时替换 syncobj 的栅栏)。

主机端等待 syncobj

DRM_IOCTL_SYNCOBJ_WAIT 采用 syncobj 句柄数组,并在主机端同时等待所有 syncobj 栅栏。 如果设置了 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL,则等待 ioctl 将等待所有 syncobj 栅栏发出信号,然后返回。 否则,它会在至少一个 syncobj 栅栏发出信号时返回,并且已发出信号的栅栏的索引会写回客户端。

与在 syncobj 中看到 NULL 栅栏时失败的排队 GPU 工作依赖项不同,如果设置了 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,则主机端等待将首先等待 syncobj 接收非 NULL 栅栏,然后等待该栅栏。 如果未设置 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT 并且数组中的任何一个 syncobj 具有 NULL 栅栏,则将返回 -EINVAL。 假设 syncobj 从 NULL 栅栏开始,这允许客户端在一个线程(或进程)中执行主机等待,该等待等待在另一个线程(或进程)中提交的 GPU 工作,而无需在两者之间手动同步。 此要求继承自 Vulkan 栅栏 API。

如果设置了 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE,则 ioctl 还将在等待之前在后备栅栏上设置栅栏截止时间提示,以便为栅栏信号发送者提供适当的紧迫感。 截止时间以纳秒为单位指定为绝对 CLOCK_MONOTONIC 值。

类似地,DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT 采用 syncobj 句柄数组以及 u64 点数组,并在主机端同时等待给定点的所有 syncobj 栅栏。

DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT 还增加了等待给定栅栏在时间线上具体化的能力,而无需使用 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE 标志等待栅栏发出信号。 此要求继承自 Vulkan 时间线信号量 API 所需的等待之前信号行为。

或者,可以使用 DRM_IOCTL_SYNCOBJ_EVENTFD 在不阻塞的情况下等待:当 syncobj 时,将发出 eventfd 信号。 这对于将等待集成到事件循环中很有用。

syncobj 的导入/导出

DRM_IOCTL_SYNCOBJ_FD_TO_HANDLEDRM_IOCTL_SYNCOBJ_HANDLE_TO_FD 提供了两种用于导入/导出 syncobj 的机制。

第一个让客户端将整个 syncobj 导入或导出到文件描述符。 这些 fd 是不透明的,除了在进程之间传递 syncobj 之外,没有其他用例。 所有导出的文件描述符和作为导入这些文件描述符的结果而创建的任何 syncobj 句柄都拥有对同一底层 struct drm_syncobj 的引用,并且可以在与之共享它的所有进程中持久地使用 syncobj。 仅当最后一个引用被删除时,才会释放 syncobj。 与 dma-buf 不同,导入 syncobj 会为每次导入创建一个新句柄(带有自己的引用),而不是去重。 此持久导入/导出的主要用例是共享 Vulkan 栅栏和信号量。

第二种导入/导出机制,由 DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILEDRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE 指示,让客户端从/向 sync_file 导入/导出 syncobj 的当前栅栏。 当 syncobj 导出到同步文件时,该同步文件会在导出时包装 syncobj 的栅栏,并且稍后对 syncobj 的任何信号或重置操作都不会影响导出的同步文件。 当同步文件导入到 syncobj 中时,syncobj 的栅栏将设置为该同步文件包装的栅栏。 因为同步文件是不可变的,所以重置或发出 syncobj 信号不会影响已将栅栏导入到 syncobj 中的任何同步文件。

时间线 syncobj 中时间线点的导入/导出

DRM_IOCTL_SYNCOBJ_TRANSFER 提供了一种机制,用于将给定 u64 点的 syncobj 的 struct dma_fence_chain 传输到另一个 syncobj 中的另一个 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 列表。

lock

保护 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

同步对象。

描述

如果 drm_syncobj.fence 包含在 obj 中且不为 NULL,则此函数会获取对它的额外引用。在没有已持有引用的情况下调用此函数是非法的。无需锁。

返回值

obj 的 fence,如果没有则为 NULL。

struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, u32 handle)

查找并引用同步对象。

参数

struct drm_file *file_private

drm 文件私有指针

u32 handle

要查找的同步对象句柄。

描述

返回指向句柄指向的 syncobj 的引用,如果找不到则返回 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)

向同步对象添加新的时间线点

参数

struct drm_syncobj *syncobj

要添加时间线点的同步对象

struct dma_fence_chain *chain

用于添加点的链节点

struct dma_fence *fence

要封装在链节点中的 fence

uint64_t point

用于点的序列号

描述

将链节点作为新的时间线点添加到同步对象。

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

超时 nsec 分量,单位为 ns,0 表示轮询

描述

从 sec/nsec 中的绝对时间计算 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);

有关更多详细信息,请参见结构体 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

控制锁定行为的标志

ticket

用于获取锁的 WW ticket

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 在锁定对象的数量内,则指向与 index 对应的锁定对象。否则为 NULL。

drm_exec_for_each_locked_object

drm_exec_for_each_locked_object (exec, index, obj)

迭代所有锁定的对象

参数

exec

drm_exec 对象

index

迭代的无符号长整型索引

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 对象

index

迭代的无符号长整型索引

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)

在此 exec 上下文中解锁 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

要保留多少个 fence

描述

通过锁定 GEM 对象并保留 fence 插槽来准备 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 对象上保留的 fence 数量

描述

准备数组中的所有 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 都携带驱动程序指定的信用额度。

一旦执行了一个作业(但尚未完成),作业的信用额度就会计入调度程序的信用计数,直到作业完成。如果通过再执行一个作业,调度程序的信用计数将超过调度程序的信用额度限制,则不会执行该作业。相反,调度程序将等待信用计数减少到足以不溢出其信用额度限制。这意味着等待先前执行的作业。

调度程序函数参考

DRM_SCHED_FENCE_DONT_PIPELINE

DRM_SCHED_FENCE_DONT_PIPELINE

防止依赖关系流水线化

描述

在此调度程序 fence 上设置此标志可以防止依赖于此 fence 的作业的流水线化。换句话说,在将依赖作业推送到硬件队列之前,我们总是插入一个完整的 CPU 往返。

DRM_SCHED_FENCE_FLAG_HAS_DEADLINE_BIT

DRM_SCHED_FENCE_FLAG_HAS_DEADLINE_BIT

已设置 fence 截止日期提示

描述

因为我们可能在后备硬件 fence 创建之前设置截止时间提示,所以我们需要跟踪是否已经设置了截止时间。

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 保护。

lock

锁定保护运行队列 (rq),此实体属于该队列,priority 和调度程序列表 (sched_list, num_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_schedulers 的数量。

priority

实体的优先级。可以通过调用 drm_sched_entity_set_priority() 来修改此值。受 lock 保护。

job_queue

此实体的作业列表。

fence_seq

随着每个新的 drm_sched_fence 的线性递增序列号,它是实体的一部分。

FIXME:drm_sched_job_arm() 的调用者需要确保正确的锁定,这不需要是原子的。

fence_context

属于此实体的所有 fence 的唯一上下文。drm_sched_fence.scheduled 使用 fence_context,但 drm_sched_fence.finished 使用 fence_context + 1。

dependency

作业队列顶部的作业的依赖 fence。

cb

上述依赖 fence 的回调。

guilty

指向实体的 guilty。

last_scheduled

指向上次调度的作业的完成 fence。仅由调度程序线程写入,如果队列为空,则可以从 drm_sched_job_arm() 无锁访问。

last_user

将作业推送到实体的最后一个组领导者。

stopped

将实体标记为从 rq 中删除,并注定要终止。这是通过调用 drm_sched_entity_flush()drm_sched_fini() 设置的。

entity_idle

当实体未被使用时发出信号,用于在 drm_sched_entity_fini() 中对实体清理进行排序。

oldest_job_waiting

标记 SW 队列中最早等待的作业

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 所属的调度程序。

lock

保护 entitiesrb_tree_rootcurrent_entity

current_entity

要调度的实体。

entities

要调度的实体列表。

rb_tree_root

用于 FIFO 调度的基于时间的实体优先级队列的根

描述

运行队列是一组实体,用于调度一个特定环的命令提交。它实现了调度策略,该策略选择下一个从中发出命令的实体。

struct drm_sched_fence

与作业调度对应的 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

此 fence 将由调度程序在调度作业时发出信号。

finished

此 fence 将由调度程序在作业完成后发出信号。

在为作业设置 out fence 时,应使用此 fence,因为它在 drm_sched_job_init() 上立即可用,并且驱动程序从 run_job() 返回的 fence 在依赖项解决之前不会被创建。

deadline

drm_sched_fence.finished 上设置的截止时间,可能需要传播到 drm_sched_fence.parent

parent

在硬件上调度作业时,由 drm_sched_backend_ops.run_job 返回的 fence。一旦父项发出信号,我们就发出 drm_sched_fence.finished fence。

sched

作业所属的调度程序实例。

lock

scheduled 和 finished fence 使用的锁。

owner

用于调试的作业所有者

struct drm_sched_job

要由实体运行的作业。

定义:

struct drm_sched_job {
    u64 id;
    ktime_t submit_ts;
    struct drm_gpu_scheduler        *sched;
    struct drm_sched_fence          *s_fence;
    struct drm_sched_entity         *entity;
    enum drm_sched_priority         s_priority;
    u32 credits;
    unsigned int                    last_dependency;
    atomic_t karma;
    struct spsc_node                queue_node;
    struct list_head                list;
    union {
        struct dma_fence_cb     finish_cb;
        struct work_struct      work;
    };
    struct dma_fence_cb             cb;
    struct xarray                   dependencies;
};

成员

id

分配给在调度程序上调度的每个作业的唯一 id。

submit_ts

作业推送到实体队列的时间。

sched

此作业将要或将要在其上调度的调度程序。由 drm_sched_job_arm() 设置。有效直到 drm_sched_backend_ops.free_job() 完成。

s_fence

包含用于作业调度的 fence。

entity

此作业所属的实体。

s_priority

作业的优先级。

credits

此作业对调度程序贡献的信用数量

last_dependency

跟踪 dependencies,因为它们会发出信号

karma

每次由该作业引起的挂起都会增加。如果此值超过调度程序的挂起限制,则该作业将被标记为 guilty,并且不会进一步调度。

queue_node

用于将此结构附加到实体中的作业队列。

list

作业参与“pending”和“done”列表。

{unnamed_union}

anonymous

finish_cb

完成 fence 的回调。

work

帮助将作业终止重新调度到不同的上下文。

cb

s_fence 中父 fence 的回调。

dependencies

包含此作业的 struct dma_fence 作为依赖项,请参阅 drm_sched_job_add_dependency()drm_sched_job_add_implicit_dependencies()

描述

作业由驱动程序使用 drm_sched_job_init() 创建,并且一旦希望调度程序调度该作业,就应调用 drm_sched_entity_push_job()

enum drm_gpu_sched_stat

调度程序的状态

常量

DRM_GPU_SCHED_STAT_NONE

保留。请勿使用。

DRM_GPU_SCHED_STAT_NOMINAL

操作成功。

DRM_GPU_SCHED_STAT_ENODEV

错误:设备不再可用。

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);
};

成员

prepare_job

当调度程序正在考虑接下来调度此作业时调用,以获取另一个 struct dma_fence,以便此作业可以阻塞。一旦它返回 NULL,就可以调用 run_job()。

如果不需要对依赖项进行额外的准备,则可以为 NULL。当作业被终止而不是运行时跳过。

run_job

一旦所有依赖项都已解决,就会调用以执行作业。

sched_job:要运行的作业

已弃用的 drm_sched_resubmit_jobs()(由 struct drm_sched_backend_ops.timedout_job 调用)可以使用相同的参数再次调用此函数。不鼓励使用此方法,因为它违反了 dma_fence 规则,特别是 dma_fence_init() 必须第二次在已初始化的 fence 上调用。此外,这是危险的,因为尝试分配内存可能会与等待重置完成的内存管理代码发生死锁。

TODO:记录驱动程序应该做什么/使用什么来代替。

此方法在工作队列上下文中调用 - 可以是从驱动程序通过 drm_sched_init() 传递的 submit_wq,或者,如果驱动程序传递了 NULL,则调度程序分配的单独的有序工作队列。

请注意,调度程序期望从回调中“继承”其自己的对此 fence 的引用。它不会在其上调用额外的 dma_fence_get()。因此,此回调必须为调度程序获取一个引用,并为驱动程序的各自需求获取额外的引用。

返回值:* 成功时:dma_fence 驱动程序必须在硬件完成作业后发出信号(“硬件 fence”)。* 失败时:NULL 或 ERR_PTR。

timedout_job

当作业执行时间过长时调用,以触发 GPU 恢复。

sched_job:已超时的作业

驱动程序通常发出重置以从 GPU 挂起中恢复。此过程看起来非常不同,具体取决于使用的是固件还是硬件调度程序。

对于固件调度程序,每个环都有一个调度程序,每个调度程序都有一个实体。因此,采取的步骤通常如下所示

  1. 使用 drm_sched_stop() 停止调度程序。这将暂停调度程序工作队列并取消超时工作,从而保证在删除环时不会排队任何内容。

  2. 删除环。固件将确保硬件的相应部分已重置,并且其他环不受影响。

  3. 终止实体和关联的调度程序。

对于硬件调度程序,调度程序实例将来自一个或多个实体的作业调度到一个环。这意味着与受影响的调度程序关联的所有实体都无法被拆除,因为这实际上也会影响未提交错误作业的无辜用户空间进程(例如)。

因此,使用硬件调度程序进行恢复的过程应如下所示

  1. 使用 drm_sched_stop() 停止受重置影响的所有调度程序。

  2. 终止错误作业源自的实体。

  3. 在所有错误的环上发出 GPU 重置(驱动程序特定)。

  4. 通过将作业重新提交到仍然处于活动状态的实体来重新提交受影响的所有调度程序上的作业。

  5. 使用 drm_sched_start() 重新启动在步骤 #1 中停止的所有调度程序。

请注意,某些 GPU 具有不同的硬件队列,但需要全局重置 GPU,这需要在不同调度程序的超时处理程序之间进行额外的同步。实现此同步的一种方法是在驱动程序级别创建一个有序工作队列(使用 alloc_ordered_workqueue()),并将此队列作为 drm_sched_init()timeout_wq 参数传递。这将保证超时处理程序按顺序执行。

返回值:调度程序的状态,由 enum drm_gpu_sched_stat 定义

free_job

一旦作业的 finished 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

一旦作业引起的挂起超过此限制,它将被标记为 guilty,并且将不再被考虑进行调度。

score

帮助负载均衡器选择空闲调度的分数

_score

当驱动程序未提供分数时使用的分数

ready

标记底层硬件是否准备好工作

free_guilty

超时处理程序释放 guilty 作业的命中。

pause_submit

暂停 submit_wqwork_run_job 的排队

own_submit_wq

调度程序拥有 submit_wq 的分配

dev

系统 struct device

描述

为每个硬件环实现一个调度程序。

struct drm_sched_init_args

用于初始化 DRM GPU 调度程序的参数

定义:

struct drm_sched_init_args {
    const struct drm_sched_backend_ops *ops;
    struct workqueue_struct *submit_wq;
    struct workqueue_struct *timeout_wq;
    u32 num_rqs;
    u32 credit_limit;
    unsigned int hang_limit;
    long timeout;
    atomic_t *score;
    const char *name;
    struct device *dev;
};

成员

ops

驱动程序提供的后端操作

submit_wq

用于提交的工作队列。如果为 NULL,则会分配并使用一个有序 wq。

timeout_wq

用于超时工作的工作队列。如果为 NULL,则使用 system_wq。

num_rqs

运行队列的数量。这最多可能是 DRM_SCHED_PRIORITY_COUNT,因为通常每个优先级都有一个运行队列,但可能更少。

credit_limit

此调度程序可以从所有作业中保存的信用数量

hang_limit

允许作业在被丢弃之前挂起的次数。此机制已弃用。将其设置为 0。

timeout

提交的作业的超时值(以 jiffies 为单位)。

score

与其他调度程序共享的分数原子。可能为 NULL。

name

名称(通常是驱动程序的名称)。用于调试

dev

关联的设备。用于调试

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

在待处理 fence 上设置的错误

描述

此函数通常用于重置恢复(有关详细信息,请参阅 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() 文档。

驱动程序必须确保 drm_sched_job_cleanup() 如果此函数成功返回,即使在调用 drm_sched_job_arm() 之前中止 job 时也是如此。

请注意,此函数不会为 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

要准备的调度程序作业

描述

这准备调度程序作业以供执行。具体来说,它初始化 jobdrm_sched_job.s_fence,以便它可以附加到 struct dma_resv 或需要跟踪此作业完成情况的其他位置。它还初始化序列号,序列号是 fence 排序的基础。

有关锁定注意事项,请参阅 drm_sched_entity_push_job() 文档。

一旦调用此函数,您必须 使用 drm_sched_entity_push_job() 提交 job

只有在 drm_sched_job_init() 成功调用后才能调用此函数。

int drm_sched_job_add_dependency(struct drm_sched_job *job, struct dma_fence *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 的 fence 添加为作业依赖项

参数

struct drm_sched_job *job

要添加依赖项的调度程序作业

struct drm_file *file

drm 文件私有指针

u32 handle

要查找的 syncobj 句柄

u32 point

时间线点

描述

这会将与给定 syncobj 匹配的 fence 添加到 job

返回值

成功时返回 0,否则返回扩展数组失败时的错误。

int drm_sched_job_add_resv_dependencies(struct drm_sched_job *job, struct dma_resv *resv, enum dma_resv_usage usage)

将 resv 中的所有 fences 添加到作业中

参数

struct drm_sched_job *job

要添加依赖项的调度程序作业

struct dma_resv *resv

要从中获取 fences 的 dma_resv 对象

enum dma_resv_usage usage

用于过滤 fences 的 dma_resv_usage

描述

这会将来自 resv 的与给定 usage 匹配的所有 fences 添加到 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

作业是否可能写入对象(因此我们需要依赖于 reservation 对象中的共享 fences)。

描述

这应该在调用 drm_gem_lock_reservations() 之后,在作业中使用的 GEM 对象数组上,但在使用您自己的 fences 更新 reservations 之前调用。

返回值

成功时返回 0,否则返回扩展数组失败时的错误。

bool drm_sched_job_has_dependency(struct drm_sched_job *job, struct dma_fence *fence)

检查 fence 是否是作业的依赖项

参数

struct drm_sched_job *job

要检查的调度程序作业

struct dma_fence *fence

要查找的 fence

返回值

如果在作业的依赖项中找到 fence,则为 True,否则为 false。

void drm_sched_job_cleanup(struct drm_sched_job *job)

清理调度程序作业资源

参数

struct drm_sched_job *job

要清理的调度程序作业

描述

清理使用 drm_sched_job_init() 分配的资源。

如果在调用 drm_sched_job_arm() 之前中止 job,则驱动程序应从其错误展开代码中调用此函数。

drm_sched_job_arm() 是一个不可返回的点,因为它初始化了 fences 及其序列号等。一旦调用了该函数,您必须使用 drm_sched_entity_push_job() 提交它,并且不能通过调用 drm_sched_job_cleanup() 简单地中止它。

此函数应在 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 sched

参数

struct drm_gpu_scheduler **sched_list

drm_gpu_schedulers 列表

unsigned int num_sched_list

sched_list 中 drm_gpu_schedulers 的数量

描述

返回负载最小的 sched 的指针,如果没有任何 drm_gpu_schedulers 准备就绪,则返回 NULL

int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_args *args)

初始化 gpu 调度程序实例

参数

struct drm_gpu_scheduler *sched

调度程序实例

const struct drm_sched_init_args *args

调度程序初始化参数

描述

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

void drm_sched_fini(struct drm_gpu_scheduler *sched)

销毁 gpu 调度程序

参数

struct drm_gpu_scheduler *sched

调度程序实例

描述

分解并清理调度程序。

这会停止通过 drm_sched_backend_ops.run_job() 向硬件提交新作业。 因此,drm_sched_backend_ops.free_job() 将不会为 drm_gpu_scheduler.pending_list 中仍有的所有作业调用。 目前没有解决此问题的方法。 因此,由驱动程序确保

  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”作业导致的每次挂起时递增。 如果这超过了调度程序的挂起限制,则会将相应的 sched 实体标记为 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 ring 时使用的上下文实体。

参数

struct drm_sched_entity *entity

要初始化的调度程序实体

enum drm_sched_priority priority

实体的优先级

struct drm_gpu_scheduler **sched_list

可以从该实体提交作业的 drm scheds 列表

unsigned int num_sched_list

sched_list 中 drm sched 的数量

atomic_t *guilty

当发现此队列上的作业因超时而 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)

修改实体的 sched

参数

struct drm_sched_entity *entity

要初始化的调度程序实体

struct drm_gpu_scheduler **sched_list

将替换现有 entity->sched_list 的新 drm scheds 列表

unsigned int num_sched_list

sched_list 中 drm sched 的数量

描述

请注意,必须在与 drm_sched_job_arm()drm_sched_entity_push_job() 相同的通用锁下调用此函数,或者驱动程序需要通过其他方式保证永远不会在可以将新作业推送到 entity 时调用此函数。

int drm_sched_entity_error(struct drm_sched_entity *entity)

返回上次调度作业的错误

参数

struct drm_sched_entity *entity

要检查的调度程序实体

描述

机会性地返回上次调度作业的错误。 当新作业推送到 hw 时,结果可以随时更改。

long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)

刷新上下文实体

参数

struct drm_sched_entity *entity

调度程序实体

long timeout

等待 Q 在 jiffies 中变为空的时间。

描述

drm_sched_entity_fini() 拆分为两个函数,第一个函数执行等待,从运行队列中删除实体,并在进程被杀死时返回错误。

返回输入超时剩余的 jiffies 时间

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

要提交的作业

注意

为了保证插入队列的顺序与作业的 fence 序列号匹配,应在 drm_sched_job_arm() 下,为 struct drm_sched_entity(在 drm_sched_job_init() 中为 sched_job 设置)的通用锁下调用此函数。