Email: BuddyZhang1 buddy.zhang@aliyun.com

目录


CMA 多块区域原理

CMA 分配器使用 cma_areas[] 数组维护所有可用的 CMA 区域。CMA 分配器可以 指定驱动使用特定的 CMA 区域。因此本文重点讨论如何在内核中支持 CMA 多 区域以及驱动如何使用特定的 CMA 区域。

CMA 多区域支持原理

内核可以采用多种方案配置一块新的 CMA 区域,这包括了下面三种方法:

每种方案各有利弊,其中 DTS 的方案可以支持多块 CMA 区域的建立,因此可以 采用 DTS 的方案来支持多块 CMA 区域。

驱动使用 CMA 原理

在内核中,每个驱动都是通过设备进行描述,每个设备使用 struct device 数据结构进行描述,其中包含了 cma_area 成员,默认情况下,该成员用于 指向一个 CMA 区域。分配连续物理内存的时候,CMA 分配器就会根据设备 的 cma_area 成员提供的信息找到指定的 CMA 区域,然后从中分配和释放 连续物理内存资源。因此要让驱动使用指定的 CMA 区域,只要设置设备 struct device 的 cma_area 成员即可


实践部署


实践基础部署

本文支持在 BiscuitOS 上实践 CMA 碎片问题,如果还没有搭建 BiscuitOS 开发环境,可以下列文章:


实践用例部署

如果已经搭建好 BiscuitOS 开发环境,本文实践部署如下:

cd BiscuitOS
make linux-5.0-arm32_defconfig
make menuconfig 

选择 “Package” 并进入二级菜单.

选择 “CMA: Contiguous Memory Allocator —>” 并进入二级菜单.

选择 “CMA on special Area Application —>” 和 “CMA on special Area Device Driver Module —>” 并保存退出。接着 使用如下命令:

make
cd BiscuitOS/output/linux-5.0-arm32/package/


实践编译部署

这里准备了对应的驱动程序和应用程序,开发者参考下面步骤进行编译安装:


驱动程序

驱动程序用于为用户空间应用程序提供 CMA 申请的接口,其使用如下:

cd BiscuitOS/output/linux-5.0-arm32/package/CMA_area_module-0.0.1
make prepare
make download

执行完上面的命令之后,BiscuitOS 会下载指定的文件,如下图:

获得上面的文件之后,开发者首先根据 “00001-CMA-special-areas.patch” 补丁 中的内容,对内核进行手动补丁或自动补丁,其修改内容如下:

从补丁的内容可以看出,首先将 “dma_alloc_from_contiguous” 和 “dma_release_from_contiguous” 两个函数使用 “EXPORT_SYMBOL()” 宏 进行导出,以便供外部模块使用。接着在 “mm/cma.c” 文件中添加了 一个函数 “find_cma_by_name()” 其作用是通过名字直到指定的 cma 区块对应的结构体. 接着是 “default.dts” 文件,该文件用户描述 该驱动的 DTS 节点以及 CMA 区块的配置信息,如下:

开发者参考上面的内容添加到内核的 DTS 文件中,例如本例子中 使用的 DTS 位于:

vi BiscuitOS/output/linux-5.0-arm32/linux/linux-5.0/arch/arm/boot/dts/vexpress-v2p-ca9.dts

开发者参考上面的 DTS 之后,在项目的 DTS 文件中找到 reserved-memory 节点, 然后向该节点中分别添加 “linux,cma” 和 “BiscuitOS_cma” 两个节点,其节点 属性与图中的配置一致。最后打完补丁和添加完 patch 之后,开发者需要重新 编译内核,可以参考如下命令:

cd BiscuitOS/output/linux-5.0-arm32/linux/linux-5.0/
make ARCH=arm CROSS_COMPILE=BiscuitOS/output/linux-5.0-arm32/arm-linux-gnueabi/arm-linux-gnueabi/bin/arm-linux-gnueabi- -j4

接着编译驱动和安装模块到 BiscuitOS,使用如下命令:

cd BiscuitOS/output/linux-5.0-arm32/package/CMA_area_module-0.0.1
make clean
make
make install
make pack

执行完上面的命令之后,驱动已经成功安装到 BiscuitOS,接着只要在 BiscuitOS 系统运行的时候安装就行。接下来是安装驱动对应的应用程序。


应用程序

应用程序与驱动程序配合,目的是从 CMA 中获得连续物理内存。从应用程序 角度看,应用程序并不关系物理地址来自哪块 CMA 区域,只关系从 CMA 中 分配到连续物理内存即可。开发者首先按照下列步骤部署应用程序:

cd BiscuitOS/output/linux-5.0-arm32/package/CMA_area_app-0.0.1
make download

在获得源码之后,开发者继续执行如下命令将应用程序源码进行编译、安装 和打包到 BiscuitOS 系统里:

make clean
make
make install
make pack


实践运行部署

在准备好驱动和应用程序之后,接下来就是在 BiscuitOS 上面使用即可。 开发者使用如下命令启动 BiscuitOS:

cd BiscuitOS/output/linux-5.0-arm32/
./RunBiscuitOS.sh

接着安装驱动:

cd lib/modules/5.0.0/extra/
insmod cma.ko
lsmod
ls -l /dev/CMA_demo

安装完毕驱动之后,可以在 “/dev” 目录下查看当前获得 CMA_demo 节点。 运行应用程序:

CMA_area_app-0.0.1

由上图可以看到应用程序从 CMA 中分配的内存起始地址是 0x69300000,该地址 位于 BiscuitOS_cma 区域内。因此实践成功。


实践源码分析

源码分作驱动部分和应用程序部分,驱动源码位于:

BiscuitOS/output/linux-5.0-arm32/package/CMA_area_module-0.0.1/CMA_area_module-0.0.1/cma.c

驱动中指定 CMA 区域的核心代码如下:

应用程序源码位于:

/xspace/OpenSource/BiscuitOS/BiscuitOS/output/linux-5.0-arm32/package/CMA_area_app-0.0.1/CMA_area_app-0.0.1/cma.c

驱动程序的主要通过应用程序 ioctl 发送命令 “CMA_MEM_ALLOCATE” 和 “CMA_MEM_RELEASE” 告诉驱动程序进行 CMA 分配和释放,驱动程序进而调用 “dma_alloc_from_contiguous()” 函数和 “dma_release_from_contiguous()” 函数 进行进行 CMA 的分配和释放动作。


附录

BiscuitOS Home

Linux Kernel

Bootlin: Elixir Cross Referencer

CMA

赞赏一下吧 🙂