CMA

news2024/11/23 20:00:05

文章目录

    • 前言
    • 概念
    • 功能启用
    • CMA 内存的创建
      • 方式一、使用 cmdline
      • 方式二、使用 dts
    • CMA 内存分配和释放
    • 实例(dts 方式)

前言

在嵌入式设备中,很多外设(如摄像机、硬件视频解码器等)需要较大的内存缓冲区,kmalloc 内存分配机制对于这么大的内存是没有效果的。
早期的 Linux 内核中,如果驱动想要申请一大块的物理连续内存,那么只能通过预留专属内存的形式,然后在驱动中使用 ioremap 来映射后作为私有内存使用。这样带来的后果就是有一部分内存将被预留出来不能作为系统中的通用内存来使用。比如 camera、audio 设备,它们在工作时是需要大块连续内存进行 DMA 操作的,而当这些设备不工作时,预留的内存也无法被其它模块使用。
引入 CMA 就是为了解决这个问题的,定义为 CMA 区域的内存,也是由操作系统来管理的,当一个驱动模块想要申请大块连续内存时,通过内存管理子系统把 CMA 区域的内存进行迁移,空出连续内存给驱动使用;而当驱动模块释放这块连续内存后,它又被归还给操作系统管理,可以给其它申请者分配使用。

概念

CMA 全称叫做 continuous memory allocator,它是为了便于进行连续物理内存申请的一块区域,一般我们把这块区域定义为 reserved-memory。
在这里插入图片描述

功能启用

Linux 内核配置 CONFIG_CMA=y

CMA 内存的创建

方式一、使用 cmdline

# cat /proc/cmdline 
root=PARTUUID=2d2a225f-07d8-4811-9802-314074f963ff rootwait console=ttyS0,115200 rootfstype=ext4 quiet panic=10 cma=12M@0x7e000000

详细参数

cma=nn[MG]@[start[MG][-end[MG]]]
[ARM,X86,KNL]
Sets the size of kernel global memory area for
contiguous memory allocations and optionally the
placement constraint by the physical address range of
memory allocations. A value of 0 disables CMA
altogether. For more information, see
include/linux/dma-contiguous.h

方式二、使用 dts

    reserved-memory {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        reserved: buffer@0 {
            compatible = "shared-dma-pool";
            reusable;
            reg = <0x60000000 0x01000000>;
            linux,cma-default;
        };

        reserved2: buffer@2 {
            compatible = "shared-dma-pool";
            reusable;
            reg = <0x62000000 0x02000000>;
            linux,cma-default;
        };
    };

compatible 的值必须是 “shared-dma-pool” 才能被解析成 CMA 类型的内存。
一定要使用 reusable 选项,才会被当作 CMA 内存,如果使用 no-map 则会被当作 DMA 内存。
如果使能了 linux,cma-default; 选项,则不会出现 cma: Reserved 16 MiB at 0x7e000000;否则,会出现。

CMA 内存分配和释放

当一个内核模块要使用 CMA 内存时,使用的接口依然是 DMA 的接口

extern void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
           gfp_t flag);

extern void
dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
            dma_addr_t dma_handle);

实例(dts 方式)

硬件:OrangePi PC

arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		reserved: buffer@0 {
			compatible = "shared-dma-pool";
			reusable;
			reg = <0x60000000 0x01000000>;
			linux,cma-default;
		};

		reserved2: buffer@2 {
			compatible = "shared-dma-pool";
			no-map;
			reg = <0x62000000 0x02000000>;
			linux,cma-default;
		};
	};

cma.ko

#include <linux/dma-mapping.h>
#include <linux/miscdevice.h>
#include <linux/module.h>

#define ALLOC_SIZE (12 * 1024)

static struct device *cma_dev;
static u32 dma_phys;
static u32 *dma_virt;

static struct miscdevice cma_test_misc = {
	.name = "cma_test",
};

static int cma_init(void)
{
	int i;
	int ret = 0;

	printk("cma_init()\n");

	ret = misc_register(&cma_test_misc);
	if (unlikely(ret)) {
		_dev_info(cma_dev, "failed to register cma test misc device!\n");
		return ret;
	}

	cma_dev = cma_test_misc.this_device;
	cma_dev->coherent_dma_mask = ~0;
	_dev_info(cma_dev, "registered\n");

	dma_virt = dma_alloc_coherent(cma_dev, ALLOC_SIZE, &dma_phys, GFP_KERNEL);
	if (dma_virt) {
		for (i = 0; i < ALLOC_SIZE / sizeof(u32); i++)
			dma_virt[i] = 0x12345678;

		_dev_info(cma_dev, "alloc virt: 0x%x phys: 0x%x size: 0x%x\n", (u32)dma_virt, dma_phys, ALLOC_SIZE);
	} else {
		_dev_info(cma_dev, "no mem in CMA area\n");
	}

	return 0;
}

static void cma_exit(void)
{
	_dev_info(cma_dev, "cma_exit()\n");

	misc_deregister(&cma_test_misc);

	_dev_info(cma_dev, "deregistered\n");

	if (dma_virt) {
		dma_free_coherent(cma_dev, ALLOC_SIZE, dma_virt, dma_phys);
		_dev_info(cma_dev, "free virt: 0x%x phys: 0x%x size: 0x%x\n", (u32)dma_virt, dma_phys, ALLOC_SIZE);
	}
}

module_init(cma_init);
module_exit(cma_exit);

MODULE_LICENSE("Dual BSD/GPL");

Makefile

obj-m = cma.o

KDIR = /home/liyongjun/project/board/buildroot/OrangePiPC/build/linux-custom
CROSS_COMPILE = /home/liyongjun/project/board/buildroot/OrangePiPC/host/bin/arm-linux-

EXTRA_CFLAGS = -g

all:
	make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules
clean:
	make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) clean

install:
	cp cma.ko ~/tftp

内核启动 log

[ 0.000000] OF: fdt: Machine model: Xunlong Orange Pi PC
[ 0.000000] Memory policy: Data cache writealloc
[ 0.000000] Reserved memory: created CMA memory pool at 0x60000000, size 16 MiB
[ 0.000000] OF: reserved mem: initialized node buffer@0, compatible id shared-dma-pool
[ 0.000000] Reserved memory: created DMA memory pool at 0x62000000, size 32 MiB
[ 0.000000] OF: reserved mem: initialized node buffer@2, compatible id shared-dma-pool
[ 0.000000] Zone ranges:
[ 0.000000] Normal [mem 0x0000000040000000-0x000000006fffffff]
[ 0.000000] HighMem [mem 0x0000000070000000-0x000000007fffffff]
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000040000000-0x0000000061ffffff]
[ 0.000000] node 0: [mem 0x0000000062000000-0x0000000063ffffff]
[ 0.000000] node 0: [mem 0x0000000064000000-0x000000007fffffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x000000007fffffff]
[ 0.000000] On node 0 totalpages: 262144
[ 0.000000] Normal zone: 1536 pages used for memmap
[ 0.000000] Normal zone: 0 pages reserved
[ 0.000000] Normal zone: 196608 pages, LIFO batch:63
[ 0.000000] HighMem zone: 65536 pages, LIFO batch:15
[ 0.000000] psci: probing for conduit method from DT.
[ 0.000000] psci: Using PSCI v0.1 Function IDs from DT
[ 0.000000] percpu: Embedded 15 pages/cpu s30988 r8192 d22260 u61440
[ 0.000000] pcpu-alloc: s30988 r8192 d22260 u61440 alloc=15*4096
[ 0.000000] pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3
[ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 260608
[ 0.000000] Kernel command line: root=PARTUUID=2d2a225f-07d8-4811-9802-314074f963ff rootwait console=ttyS0,115200 rootfstype=ext4 quiet panic=10
[ 0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes, linear)
[ 0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes, linear)
[ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[ 0.000000] Memory: 977608K/1048576K available (7168K kernel code, 904K rwdata, 1944K rodata, 1024K init, 273K bss, 54584K reserved, 16384K cma-reserved, 262132K highmem)

从内核启动 log 可知,内核在物理地址 0x60000000 处分配了 CMA 内存,大小为 16M。

ko 测试

Welcome to Buildroot for the Orange Pi PC
OrangePi_PC login: root
# tftp -gr cma.ko 192.168.31.223
# echo 7 > /proc/sys/kernel/printk
#
# insmod cma.ko 
[   45.013959] cma: loading out-of-tree module taints kernel.
[   45.020024] cma_init()
[   45.022643] misc cma_test: registered
[   45.026793] misc cma_test: alloc virt: 0xe005c000 phys: 0x6005c000 size: 0x3000
#
# devmem 0x6005c000
0x12345678
#
# devmem 0x6005effc
0x12345678
#
# devmem 0x6005f000
0xFFFFFFFF
#
# rmmod cma
[  109.603006] misc cma_test: cma_exit()
[  109.607098] misc cma_test: deregistered
[  109.610970] misc cma_test: free virt: 0xe005c000 phys: 0x6005c000 size: 0x3000

在 cma.ko 中,使用 CMA API 从 CMA 内存中申请了 12M,即 phys: 0x6005c000 size: 0x3000,并将这块区域按 int 的类型全部赋值为 0x12345678,后续使用 devmem 验证该操作成功。
另外,我们还使用 “no-map” 的方式申请了一部分 reserved-memory 作为 DMA 内存,这点可以通过 /proc/iomem 信息得到验证(内核启动 log 也可以佐证),System RAM 被截断成了两部分,而中间缺失的那部分 0x62000000-0x63ffffff 正是被我们设置成了 reserved-memory。

40000000-61ffffff : System RAM
64000000-7fffffff : System RAM

# cat /proc/iomem
01000000-0100ffff : 1000000.clock clock@1000000
01100000-011fffff : 1100000.mixer mixer@1100000
01400000-0141ffff : 1400000.deinterlace deinterlace@1400000
01c00000-01c00fff : 1c00000.system-control system-control@1c00000
01c02000-01c02fff : 1c02000.dma-controller dma-controller@1c02000
01c0c000-01c0cfff : 1c0c000.lcd-controller lcd-controller@1c0c000
01c0e000-01c0efff : 1c0e000.video-codec video-codec@1c0e000
01c0f000-01c0ffff : 1c0f000.mmc mmc@1c0f000
01c14000-01c143ff : 1c14000.eeprom eeprom@1c14000
01c15000-01c15fff : 1c15000.crypto crypto@1c15000
01c17000-01c17fff : 1c17000.mailbox mailbox@1c17000
01c19000-01c193ff : usb@1c19000
  01c19000-01c193ff : musb-hdrc.4.auto usb@1c19000
01c19400-01c1942b : 1c19400.phy phy_ctrl
01c1a000-01c1a0ff : 1c1a000.usb usb@1c1a000
01c1a400-01c1a4ff : 1c1a400.usb usb@1c1a400
01c1a800-01c1a803 : 1c19400.phy pmu0
01c1b000-01c1b0ff : 1c1b000.usb usb@1c1b000
01c1b400-01c1b4ff : 1c1b400.usb usb@1c1b400
01c1b800-01c1b803 : 1c19400.phy pmu1
01c1c000-01c1c0ff : 1c1c000.usb usb@1c1c000
01c1c400-01c1c4ff : 1c1c400.usb usb@1c1c400
01c1c800-01c1c803 : 1c19400.phy pmu2
01c1d000-01c1d0ff : 1c1d000.usb usb@1c1d000
01c1d400-01c1d4ff : 1c1d400.usb usb@1c1d400
01c1d800-01c1d803 : 1c19400.phy pmu3
01c20000-01c203ff : clock@1c20000
01c20800-01c20bff : 1c20800.pinctrl pinctrl@1c20800
01c20ca0-01c20cbf : 1c20ca0.watchdog watchdog@1c20ca0
01c22c00-01c22fff : 1c22c00.codec codec@1c22c00
01c25000-01c253ff : 1c25000.thermal-sensor thermal-sensor@1c25000
01c28000-01c2801f : serial
01c30000-01c3ffff : 1c30000.ethernet ethernet@1c30000
01c40000-01c4ffff : 1c40000.gpu gpu@1c40000
01ee0000-01eeffff : 1ee0000.hdmi hdmi@1ee0000
01ef0000-01efffff : 1ef0000.hdmi-phy hdmi-phy@1ef0000
01f00000-01f003ff : rtc@1f00000
01f01400-01f014ff : clock@1f01400
01f015c0-01f015c3 : 1f015c0.codec-analog codec-analog@1f015c0
01f02000-01f023ff : 1f02000.ir ir@1f02000
01f02400-01f027ff : 1f02400.i2c i2c@1f02400
01f02c00-01f02fff : 1f02c00.pinctrl pinctrl@1f02c00
40000000-61ffffff : System RAM
  40008000-40afffff : Kernel code
  40c00000-40d26637 : Kernel data
64000000-7fffffff : System RAM

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/714850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

clickhouse日志表占用大量磁盘空间

clickhouse日志表占用大量磁盘空间 sql&#xff1a; SELECT sum(rows) AS 总行数, formatReadableSize(sum(data_uncompressed_bytes)) AS 原始大小, formatReadableSize(sum(data_compressed_bytes)) AS 压缩大小, round((sum(data_compressed_bytes) / sum(data_uncompresse…

<DB2> 《IBM DB2 备份恢复实用文档》(第一部分)

[TOC](《IBM DB2 备份恢复实用文档》(第一部分)) 1 理论 1.1 关于备份恢复说明 a、DB2数据库备份和恢复的数据都是已经提交落地在磁盘的数据 。 b、DB2数据库备份和恢复使用的日志都是归档日志。 c、只有开启归档日志&#xff0c;才能进行在线全备、在线增备。否则只能进行离…

基于单片机智能手环心率老人防跌倒心率体温 步数里程

功能介绍 以STM32单片机作为主控系统&#xff1b; OLED液晶显示心率体温步数等信息&#xff1b;通过按键设置心率、体温上限设置&#xff1b;当心率或者体温超过按键设置上限蜂鸣器进行声光报警提醒&#xff1b;通过wifi模块esp8266把数据发送到手机端进行显整个电路以5v供电&a…

Three.js卡通材质实现简明教程

继 Harry Alisavakis 令人惊叹的汤着色器之后&#xff0c;我想使用 Three.js 重新创建类似的卡通着色效果。 我从 Roystan 的卡通着色器教程开始&#xff0c;它是为 Unity 编写的。 在这篇文章中&#xff0c;我将把 Roystan 教程中概述的原则翻译成 Three.js。 下面描述的着色器…

mysql索引之Hash

在存储引擎中Memory引擎是支持Hash索引的&#xff0c;Hash索引跟java中的HashMap很像&#xff0c;有很多槽&#xff0c;存的也是键值对&#xff0c;键值为索引列&#xff0c;值为这条数据的行指针&#xff0c;通过指针就可以找到数据。 但是Hash索引应用的并不多&#xff0c;原…

一篇文章解释清楚IOC和DI

背景 众所周知我们要学习Spring&#xff0c;必不可少的就是IOC和AOP&#xff0c;那就让我们了解一下什么是IOC&#xff0c;开启下面的学习吧。 过程 什么是IOC&#xff1f; Ioc—Inversion of Control&#xff0c;即“控制反转”&#xff0c;不是什么技术&#xff0c;而是一…

VSCode 2019 “对COM组件的调用返回了错误HRESULT E_FAIL” 的解决

问题&#xff1a; VSCode使用 “MFC应用”模板创建项目时&#xff0c;出现&#xff1a;文件夹打不开&#xff0c;并弹出 “对COM组件的调用返回了错误HRESULT E_FAIL” 错误 解决方案&#xff1a; 1. 以管理员身份打开Developer Command Prompt for VS 2019&#xff08;vs2…

敏捷开发发展和优缺点

目录 1 概述1.1 四种开发模式1.1.1 瀑布式开发1.1.2 螺旋模型1.1.3 迭代式开发1.1.4 敏捷开发 1.2 开发模式对比 2 敏捷开发2.1 敏捷宣言2.1.1 敏捷宣言解读2.1.2 敏捷宣言价值观 2.2 敏捷准则2.2.1 目的&#xff1a;是客户满意2.2.2 态度&#xff1a;欢迎需求变更2.2.3 关注&a…

加油,也可以更智慧

摘要&#xff1a;智慧加油站及油库管理系统的应用引擎是结合了华为云Roma Exchange能力&#xff0c;提升应用开发、部署和升级效率&#xff0c;支撑应用快速开发、远程部署。 停车、加油、驶离…… 从开车进场到离场&#xff0c;2分钟内即可完成“即加即走”的无感加油支付有没…

如何自动批量查询手机号归属地?

我们在工作生活中可能会收集到很多用户的手机号&#xff0c;我们如果想获取手机号归属地&#xff0c;只能一个个人工查询。如果数据量较多的情况就会比较耗费时间。有没有什么方法可以自动查询手机号归属地呢&#xff1f;当然可以&#xff0c;并且这个方法还是免费的。 首先&a…

qt-线程竞争共享资源和读写锁--QReadWriteLock

目录 一、线程竞争的概念2、什么是线程竞争2、什么是线程竞争共享资源&#xff1f; 二、读写锁1、读写锁的概念2、读写锁的工作原理如下&#xff1a;3、使用读写锁的示例&#xff08;QReadWriteLock&#xff09; 三、总结&#xff1a; 一、线程竞争的概念 2、什么是线程竞争 …

网络安全进阶学习第五课——文件上传漏洞

文章目录 一、常见文件上传点二、任意文件上传漏洞三、任意文件上传危害四、webshell五、上传木马所需条件六、木马上传流程七、上传绕过1、绕过JS验证1&#xff09;Burpsuite剔除响应JS。2&#xff09;浏览器审计工具剔除JS 2、绕过MIME-Type验证1&#xff09;利用抓包工具&am…

Session 反序列化漏洞

将$_SESSION中保存的所有数据序列化存储到PHPSESSID对应的文件中有三种存取格式&#xff1a; &#xff08;1&#xff09;默认使用php&#xff1a;键名|键值&#xff08;经过序列化函数处理的值&#xff09; name|s:6:"1FonlY"; &#xff08;2&#xff09;php_seri…

Multi-level Wavelet-CNN for Image Restoration论文总结

论文&#xff1a;Multi-level Wavelet-CNN for Image Restoration 源码&#xff1a;GitHub - lpj0/MWCNN: Multi-level Wavelet-CNN for Image Restoration 目录 一、背景和出发点 二、创新点 三、MWCNN具体实现 四、DWT与池化运算和膨胀卷积相关性证明 五、DWT、IWT代码实…

阿里云服务器白嫖教程

阿里云服务器白嫖教程 第一步:打开百度第二步:进入阿里云官方,注册登录账号第三步:点击免费试用第四步:点击立即试用第五步:选择操作系统第五步:选择到期释放设置![在这里插入图片描述](https://img-blog.csdnimg.cn/d02f4582dd5943319441df9ccbae60f0.png)第六步:同意协议,并立…

3D深度视觉与myCobot 320机械臂无序抓取

今天我记录使用myCobot320 M5跟FS820-E1深度相机进行一个无序抓取物体的分享。 为什么会选择深度相机和机械臂做一个案例呢&#xff1f; 2D相机&#xff08;最常见使用的相机&#xff09;可以捕捉二维图像&#xff0c;也就是在水平和垂直方向上的像素值。它们通常用于拍摄静态…

F#奇妙游(5):计算π的值

F#到底有什么用&#xff1f; 奇妙游写到第五篇&#xff0c;前面的几篇都是开场白&#xff1a; 一个用F#编写WinForm的例子donet命令行工具&#xff0c;也就是F#的开发环境关于函数和函数式编程的碎碎念函数式编程的核心概念&#xff1a;值 下面&#xff0c;我们开始正式来搞…

数据库左、右、内、逗号、全连接(mysql不包含全连接)方式

1、准备数据 学生有归属班级 学生表 班级表 2、执行查询语句 2.1执行左关联 select * from student stu left join class cla on (stu.class_idcla.class_id); 结果如下 2.2执行右关联 2.3、执行内连接 2.4执行逗号分隔表的连接方式 和内连接的查询结果是一样的

实训笔记7.3

实训笔记7.3 7.3一、座右铭二、单例模式三、IDEA集成开发环境的安装和基本使用四、Debug断点调试4.1 作用有两个4.2 用法&#xff1a;4.3 IDEA设置step into进入JDK源码4.4 step over4.5 step into 五、Java中的网络编程5.1 网络编程的三个核心要素5.2 通过Java实现网络编程 7.…

第三章 搜索与图论(二)——最短路问题

文章目录 单源最短路朴素Dijkstra堆优化版DijkstraBellman Ford算法SPFASPFA求负环 多源汇最短路Floyd 最短路练习题849. Dijkstra求最短路 I850. Dijkstra求最短路 II853. 有边数限制的最短路851. spfa求最短路852. spfa判断负环854. Floyd求最短路 源点表示起点&#xff0c;汇…