LWN:ION变了个形,就要打入Linux内部了!

Destaging ION

July 9, 2019

This article was contributed by Marta Rybczyńska

过去几年中,Android系统里面已经使用过多种DMA buffer分配方案了。首先是PMEM,然后被ION所替换。ION自从2012年开始商用以来,一直存在于kernel的staging代码里。从2013年就开始试着把ION加入Linux mainline,那时还有不少问题,因此没能合入。最近,John Stultz发布了一个patch set,实现了DMA-BUF heap,是ION的一个演进版本,希望能借此来把Android的DMA-buffer allocator推入Linux内核代码。

需要跟硬件设备进行交互的application通常都需要有一个memory buffer能跟设备驱动程序共享数据。最理想情况是这个buffer能够直接是通过memory map得到,并且是物理连续的,这样就能让DMA直接读写这块buffer,减少CPU或者外设来访问内存的开销。ION就是用来支持这种场景的,它实现了统一的API来定义、共享这类memory buffer,同时也把外设和平台对这种场景的一些限制考虑了进来。

ION提供了多个memory pool,称为“heaps”。它们有不同的属性,例如有的是物理连续的,有的不是。这些heap包括“system”heap,用来供vmalloc_user()分配;“system_contig”h eap,是用kzalloc()分配的。还有“carveout” heap,是在启动阶段就额外划分出来的物理连续区域。应用程序可以用一些user space的API来对这些heap分配、释放、共享内存。

ION开发的时候,kernel也有一个DMA buffer sharing (DMA-BUF)的API在同时进行开发,此外还有contiguous memory allocator (CMA)。所以功能上有些重复。此外,ION起初是在32-bit ARM处理器上为Android开发的,所以用了一些ARM特有的kernel API。这些都是合入linux mainline的阻碍。而这个新的基于DMA-BUF heap的patch就对ION的内部实现做了彻底的重构,使用CMA来基于某个特殊内存区域实现物理连续heap,也不再使用ARM特有的函数了。patch set还带了一个self-test,展示了API是如何使用的。

Heaps and allocations

每个heap都在/dev/dma_heap目录下有个对应的特殊文件。应用程序可以打开某个heap文件,然后就能从heap分配buffer了。相关的分配函数是DMA_HEAP_IOC_ALLOC这个ioctl()。有一个参数,是指向dma_heap_allocation_data结构的一个指针。

struct dma_heap_allocation_data {
        __u64 len;
        __u32 fd;
        __u32 fd_flags;
        __u64 heap_flags;
        __u32 reserved0;
        __u32 reserved1;
    };

len是分配的byte数,fd在结构初始化的时候是0,在DMA_HEAP_IOC_ALLOC操作完成后会填入一个文件描述符,代表分配得到的DMA-BUF。fd_flags描述了文件描述符的一些配置,例如O_CLOEXEC,O_RDONLY,O_WRONLY,O_RDWR等,而heap_flags存放着传递给heap allocator的标志,应该设为0。最后还有2个reserved参数,也都设为0即可。ioctl()在成功后会返回0。

应用程序需要对返回的buffer的文件描述符调用mmap(),这样就能访问分配到的内存了。如果buffer后面不再需要用了,可以直接close这个文件描述符,就能释放这块内存了。

总之,application所用到的每个heap都有一个相关的文件描述符,每个分配出来的buffer也有对应的文件描述符。DMA-BUF heap里得来的buffer handle都是通用的DMA-BUF handle,可以直接传递给其他任何能处理这类buffer的驱动程序。这个API跟此前的ION方案不太一样,当时的分配器只有一个特殊文件节点来总管这些分配工作,而且buffer对应的handle都是各种非标准的handle。当时还有一个专用的ioctl()才能实现memory的共享,现在DMA-BUF heap里没有了。

Memory access synchronization

处理CPU和设备共享的buffer的时候,有一类比较复杂的问题,就是决定在某个时刻谁(device, or CPU)有权限访问这块buffer。这主要是跟cache的存在有关系。CPU访问memory的时候通常需要经过cache,而device访问memory就不需要。同时访问的话,可能会导致cache和内存里的数据对不上,从而导致数据错乱。相应的解决方案就是让driver和application在要读写shared memory的时候都要明确告知对方,这样才能让kernel维护好cache内容。

此前的ION方案不会管理这个双方同步的问题。DMA-BUF heap会用DMA-BUF API来实现同步。具体来说就是DMA_BUF_IOCTL_SYNC ioctl(),会传入一个包含flag的结构来描述具体的同步信息。在访问一个share buffer之前,application会先用DMA_BUF_SYNC_START flag,并且附带相关的访问模式(DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, 或者DMA_BUF_SYNC_RW就是前两者的组合)。访问结束之后,就用DMA_BUF_SYNC_END带着相同的flag来告知对方。

Available heaps and adding new ones

目前的实现方案里采用了模块化的方式来管理heap。定义了一个通用的框架,来管理每类heap的实现。patch set里面主要提供了两类heap:使用alloc_page()的system heap,还有使用CMA allocator的“cma” heap(如果系统里有这个heap的话)。

跟此前的ION方案类似,也是需要application开发者来选择应该用哪个heap,因为这个需要跟他要用到的各个device的需求有关。这算是一个限制吧,不过这个问题很复杂,Linux mainline上 也没有什么现成的方案能解决此问题。在嵌入式系统的环境里,通常都会使用DMA heap,并且硬件的配置也都比较固定,所以device需要什么样的memory,在application开发之前对开发者都是已知的了。

Kernel开发者也得到了一个框架,用于添加新的heap,不过目前只能在系统启动的时候一次性加进来。每个heap都需要填写一个operation structure并且对外公告出去。。这个结构就是struct dma_heap_ops目前还很简单,只包含如下一个函数:

struct dma_heap_ops {
        int (*allocate)(struct dma_heap *heap,
                        unsigned long len,
                        unsigned long fd_flags,
                        unsigned long heap_flags);
    };

用于对外公告的structure格式如下:

struct dma_heap_export_info {
        const char *name;
        struct dma_heap_ops *ops;
        void *priv;
    };

name就是heap的名字,ops就是指向上面那个odma_heap_ops结构的指针,priv可以放置heap相关的数据。这些参数跟DMA_HEAP_IOC_ALLOC ioctl()的结构完全一样。allocator返回的就是对应这个分配好的DMA-BUF的文件描述符。

填好上述两个结构之后,heap的底层实现需要调用下面这个函数来增加heap:

struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info);

Next steps

自从ION第一次出现以来,Linux kernel有了很多发展,也拥有了更完善的支撑函数,可以用在新的实现方案里了。DMA-BUF heap接口非常简单,这个patch set也特意没有包含某些功能(例如更多的heap类型)和性能优化部分。它的目的主要是要定义清楚API。优化和更多功能都可以今后再加进来。合理的下一步行为就是来对allocation阶段的性能进行优化。Stultz已经有patch了,不过决定先不提交,从而加快review流程。分配的性能应该是可以达到甚至超过此前ION方案的。

目前这个patch set已经达到了第6版,看起来目前的这个简单的、逐步推进的方案看起来很有效,相关的讨论也都进行的很顺利。很有可能不久之后就能看到kernel合入这个新的API了。

全文完

LWN文章遵循CC BY-SA 4.0许可协议。

极度欢迎将文章分享到朋友圈 

长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章