1 概述
本文主要介绍在Linux中如何预留出一块内存,以及预留内存的原理与使用方式,只停留在应用阶段,因为涉及Linux内存管理,没有深入Linux对预留管理的基础原理和源码,留待后续Linux内存管理章节分析。读者若只想了解如何在Linux下预留内存的应用,本文比较合适。
本文验证环境使用qemu模拟vpress平台做的,关于该环境的搭建,参考笔者之前写的文章:
使用qemu搭建armv7嵌入式开发环境_qemu armv7-优快云博客
2 Linux下的CMA
2.1 CMA
Linux内核的连续内存分配器 (CMA),Contiguous Memory Allocator,是内存管理子系统中的一个模块,负责物理地址连续的内存分配。CMA的核心并不是设计精巧的算法来管理地址连续的内存块,实际上它的底层还是依赖内核伙伴系统这样的内存管理机制,或者说CMA是处于需要连续内存块的其他内核模块(例如DMA mapping framework)和内存管理模块之间的一个中间层模块。
CMA同时也用于解决大内存预留带来的内存碎片,同时支持在预留内存空闲时返回系统使用,充分利用内存空间。
2.2 预留内存的声明
预留内存可以指定常用的属性,常见有:
(1). reusable:表示当前的内存区域除了被dma使用之外,还可以被内存管理(buddy)子系统reuse。
(2). no-map:表示是否需要创建页表映射,对于通用的内存,必须要创建映射才可以使用,共享CMA是可以作为通用内存进行分配使用的,因此必须要创建页表映射。
(3). linux,cma-default:如果要cma区域为共享区域,需要配置上linux,cma-default属性。 指定了 linux,cma-default 属性,内核在分配 cma 内存时会将这片内存当成默认的 cma 分配池使用,执行内存申请时如果没有指定对应的 cma 就使用默认 cma pool。
(4). alignment:对齐参数,保留内存的起始地址需要向该参数对齐
(5). alloc-ranges:指定可以用来申请动态保留内存的区间.
(6). shared-dma-pool(compatible="shared-dma-pool"):
有的时候设备驱动程序需要采用DMA的方式使用预留的内存,对于这种场景,可以dts中的节点属性设置为shared-dma-pool,从而生成为特定设备驱动程序预留的DMA内存池。这样,设备驱动程序仅需要以常规方式使用DMA API。
2.3 内核配置
Linux要支持CMA内存,通过dma接口使用CMA内存池内存,需要打开以下配置:
CONFIG_DMA_CMA=y
CONFIG_CMA=y
也可大小相关调试开关
CONFIG_CMA_DEBUG=y
CONFIG_CMA_DEBUGFS=y
3 Linux下使用cma
本节主要介绍驱动如何使用Linux的cam来预留内存方法,分别有预留内存给专门的设备驱动、通过DMA的API来预留内存、使用CAM内存池预留内存。同时Linux也支持通过启动参数来预留内存。
3.1 预留内存给专门设备驱动
预留内存给设备驱动,不再将这些内存给系统使用时,需要在设备树申明no-map。如下设备树声明:
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
...
test_reserver_sh:test_reserver_sh_region@70000000 {
compatible = "shared-dma-pool";
reg = <0x70000000 0x6000000>;
no-map;
};
...
};
camsh_lab:camsh_lab {
compatible = "test,cam_m_sh";
memory-region = <&test_reserver_sh>;
status = "okay";
};
在代码中使用:
static struct cma_lab_ampdata cmash_lab_amp = {
.name = "lab_of_cma_sh",
.type = TEST_CMA_SHARED,
};
static const struct of_device_id memory_cam_lab_match[] = {
{
.compatible = "test,cam_m",
.data = &cma_lab_amp,
},
{
.compatible = "test,cam_m_sh",
.data = &cmash_lab_amp,
},
{
.compatible = "test,cam_m_pool",
.data = &cmash_lab_pool,
},
{}
};
MODULE_DEVICE_TABLE(of, memory_cam_lab_match);
static int cma_lab_probe(struct platform_device *pdev)
{
const struct cma_lab_ampdata *cma_match_data = NULL;
struct device *dev = &pdev->dev;
unsigned int dma_addr = 0;
void *addr;
struct device_node* dev_node;
struct resource res;
void* base;
int ret = 0;
dev_info(dev, "cma data probe hit\n");
cma_match_data = of_device_get_match_data(&pdev->