《Linux DRM Developer's Guide》学习笔记--内存管理

现代 Linux 系统需要大量的图形内存来存储帧缓存、纹理、顶点和其他与图形相关的数据。考虑到许多数据的动态特性, 有效地管理图形内存对图形栈至关重要, 并在 DRM 基础结构中发挥中心作用。
   GEM 主要是对 FrameBuffer 的管理,如显存的申请释放 (Framebuffer managing) ,显存共享机制 (Memory sharing objects), 及显存同步机制 (Memory synchronization),而KMS 主要是完成显卡配置 (Display mode setting) .

**DRM 核心包括两个内存管理器, 即平移表映射 (TTM) 和图形执行管理 (GEM)。**TTM 是第一个被开发的 DRM 内存管理器, 并试图成为一个大小适合他们的所有解决方案。它提供了一个单一的 userspace API, 以满足所有硬件的需要, 同时支持统一内存体系结构 () 设备和具有专用视频 RAM (即大多数离散显卡) 的设备。这导致了一个大的, 复杂的代码段, 结果是很难用于驱动程序开发。

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

The Graphics Execution Manager (GEM)

GEM is data-agnostic. It manages abstract buffer objects without knowing what individual buffers contain. APIs that require knowledge of buffer contents or purpose, such as buffer allocation or synchronization primitives, are thus outside of the scope of GEM and must be implemented using driver-specific ioctls.

On a fundamental level, GEM involves several operations:

  • Memory allocation and freeing (内存管理和释放)
  • Command execution (命令执行)
  • Aperture management at command execution time(命令执行时的aperture管理)

Buffer object allocation is relatively straightforward and largely provided by Linux’s shmem layer, which provides memory to back each object.

Device-specific operations, such as command execution, pinning, buffer read & write, mapping, and domain ownership transfers are left to driver-specific ioctls.

DRM初始化

   使用 GEM 的驱动程序必须将结构 struct drm_driver driver_features 字段中设置为DRIVER_GEM 。然后, DRM 核心将在调用加载操作之前自动初始化GEM 内核。最后,这将创建一个 DRM 内存管理器对象, 它为对象分配提供了一个地址空间池。
  在 KMS 配置中, 如果硬件需要, 在核心 GEM 初始化后驱动程序需要分配和初始化command ring buffer。帧设备通常有所谓的stolen memory region, 它为设备所需的初始和大的连续内存区域提供空间。此空间通常不由 GEM 管理, 必须单独初始化为其自己的 DRM MM 对象。
  UMA(Unified Memory Access ) 设备通常又被称之为stolen memory region,它为设备所需的初始和大的连续内存区域提供空间。此空间通常不由 GEM 管理,必须单独初始化into its own DRM MM object.

GEM Objects Creation

  GEM objects are represented by an instance of struct struct drm_gem_object. Drivers usually need to extend GEM objects with private information and thus create a driver-specific GEM object structure type that embeds an instance of struct struct drm_gem_object.
  To create a GEM object, a driver allocates memory for an instance of its specific GEM object type and initializes the embedded struct struct drm_gem_object with a call to drm_gem_object_init(). The function takes a pointer to the DRM device, a pointer to the GEM object and the buffer object size in bytes.
  Drivers are responsible for the actual physical pages allocation by calling shmem**_read_mapping_page_gfp()** for each page. Note that they can decide to allocate pages when initializing the GEM object, or to delay allocation until the memory is needed (for instance when a page fault occurs as a result of a userspace memory access or when the driver needs to start a DMA transfer involving the memory).
  Anonymous pageable memory allocation is not always desired, for instance when the hardware requires physically contiguous system memory as is often the case in embedded devices. Drivers can create GEM objects with no shmfs backing (called private GEM objects) by initializing them with a call to drm_gem_private_object_init() instead of drm_gem_object_init(). Storage for private GEM objects must be managed by drivers.

GEM Objects Lifetime

  All GEM objects are reference-counted by the GEM core. References can be acquired and release by calling drm_gem_object_get() and drm_gem_object_put() respectively. The caller must hold the struct drm_device struct_mutex lock when calling drm_gem_object_get(). As a convenience, GEM provides drm_gem_object_put_unlocked() functions that can be called without holding the lock.
  When the last reference to a GEM object is released the GEM core calls the struct drm_driver gem_free_object_unlocked operation. That operation is mandatory for GEM-enabled drivers and must free the GEM object and all associated resources.
  void (*gem_free_object) (struct drm_gem_object *obj); Drivers are responsible for freeing all GEM object resources. This includes the resources created by the GEM core, which need to be released with drm_gem_object_release().

  To create a handle for a GEM object drivers call drm_gem_handle_create(). The function takes a pointer to the DRM file and the GEM object and returns a locally unique handle. When the handle is no longer needed drivers delete it with a call to drm_gem_handle_delete(). Finally the GEM object associated with a handle can be retrieved by a call to drm_gem_object_lookup().

GEM Objects mapping

  mmap 系统调用不能直接用于映射 GEM 对象, 因为它们没有自己的文件句柄。目前有两种替代方法可以将GEM对象映射到用户态。第一种方法使用特定于驱动程序的 ioctl 来执行映射操作, 调用do_mmap() 。这个方案还有争议,有待商榷。
  第二种方法使用 DRM 文件句柄上的 mmap 系统调用。void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); DRM 标识要通过 mmap 偏移参数传递的假偏移来映射的 GEM 对象。在映射之前, GEM 对象必须与the fake offset关联。为此, 驱动程序必须在对象上调用 drm_gem_create_mmap_offset() 。一旦分配, the fake offset必须以特定于驱动程序的方式传递给应用程序, 然后可以用作 mmap 偏移参数。
  GEM 内核提供了一个帮助器方法 drm_gem_mmap() 来处理对象映射。该方法可以直接设置为 mmap 文件操作处理程序。它将基于偏移值查找 GEM 对象, 并将 VMA 操作设置为 struct drm_driver gem_vm_ops 字段。请注意, drm_gem_mmap() 不将内存映射到用户空间, 而是依赖于驱动程序提供的错误处理程序来单独映射页。
  要使用 drm_gem_mmap(), 驱动程序必须用指向 vm 操作的指针填充结构 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);
        int (*fault)(struct vm_fault *vmf);
};

  打开和关闭操作必须更新 GEM 对象引用计数。驱动程序可以将 drm_gem_vm_open() 和 drm_gem_vm_close() 助手功能直接用作打开和关闭处理程序。

Command Execution

  也许 GPU 设备最重要的 GEM 功能是为客户端提供一个命令执行界面。客户端程序构造包含对以前分配的内存对象的引用的命令缓冲区, 然后将它们提交到 GEM。此时, GEM 将注意将所有对象绑定到 GTT 中, 执行缓冲区, 并在访问相同缓冲区的客户端之间提供必要的同步。这通常包括从 GTT 中逐出一些对象并重新绑定其他人 (一个相当昂贵的操作), 并提供重定位支持, 它隐藏了来自客户端的固定 GTT 偏移量。客户端必须注意不要提交引用更多对象的命令缓冲区, 而不能满足 GTT 的要求。否则, GEM 将拒绝它们, 并且不会出现渲染。类似地, 如果缓冲区中的几个对象要求为正确呈现 (例如, pre-965 芯片上的 2D blits) 分配围栏寄存器, 则必须注意不要要求更多的围栏寄存器, 而不能提供给客户端。此类资源管理应从 libdrm 中的客户中提取。

VMA Offset Manager

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

Look up node by exact address

void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager * mgr)

Lock lookup for extended private use

void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager * mgr)

Unlock lookup for extended private use

void drm_vma_node_reset(struct drm_vma_offset_node * node)

Initialize or reset node object

unsigned long drm_vma_node_start(const struct drm_vma_offset_node * node)

Return start address for page-based addressing

unsigned long drm_vma_node_size(struct drm_vma_offset_node * node)

Return size (page-based)

__u64 drm_vma_node_offset_addr(struct drm_vma_offset_node * node)

Return sanitized offset for user-space mmaps

DRM MM Range Allocator

  **drm_mm**provides a simple range allocator. 只要合适,驱动可以自由在linux内核中使用这个资源分配器,drm_mm 的好处是它在DRM core,这意味着它可以更容易的扩展对于GPU的一些特殊的需求。
  核心结构体:drm_mm, allocations are tracked in drm_mm_node. Drivers are free to embed either of them into their own suitable datastructures. drm_mm itself will not do any memory allocations of its own, so if drivers choose not to embed nodes they need to still allocate them themselves.

你可能感兴趣的:(《Linux DRM Developer's Guide》学习笔记--内存管理)