linux中直接内存访问(DMA)

Linux内存管理 —— DMA和一致性缓存 1. 出现内存不一致的原因 CPU写内存的时候有两种方式: 1. write through: CPU直接内存,不经过cache。 2. write back: CPU只写到cache中。cache的硬件使用LRU算法将cache里面的内容替换到内存。通常是这种方式。 DMA可以完成从内存到外设直接进行数据搬移。但DMA不能访问CPU的cache,CPU在读内存的时候,如果cache命中... 阅读详情

1. 什么是DMA

直接内存访问是一种硬件机制,它允许外围设备和主内存之间直接传输它们的I/O数据,而不需要系统处理器的参与。使用这种机制可以大大提高与设备通信的吞吐量。

 

2. DMA数据传输

有两种方式引发数据传输:

第一种情况:软件对数据的请求

1. 当进程调用read,驱动程序函数分配一个DMA缓冲区,并让硬件将数据传输到这个缓冲区中。进程处于睡眠状态。

2. 硬件将数据写入到DMA缓冲区中,当写入完毕,产生一个中断

3. 中断处理程序获取输入的数据,应答中断,并唤起进程,该进程现在即可读取数据

第二种情况发生在异步使用DMA时。

1. 硬件产生中断,宣告新数据的到来

2. 中断处理程序分配一个缓冲区,并且告诉硬件向哪里传输数据

3. 外围设备将数据写入数据区,完成后,产生另外一个中断

4.处理程序分发新数据,唤醒任何相关进程,然后执行清理工作

 

高效的DMA处理依赖于中断报告。

 

3. 分配DMA缓冲区

使用DMA缓冲区的主要问题是:当大于一页时,它们必须占据连续的物理页,因为设备使用ISA或PCI系统总线传输数据,而这两种方式使用的都是物理地址。

使用get_free_pasges可以分配多大几M字节的内存(MAX_ORDER是11),但是对于较大数量(即使是远小于128KB)的请求,通常会失败,这是因为系统内存充满了内存碎片。

解决方法之一就是在引导时分配内存,或者为缓冲区保留顶部物理内存。

例子:在系统引导时,向内核传递参数“mem=value”的方法保留顶部的RAM。比如系统有256内存,参数“mem=255M”,使内核不能使用顶部的1M字节。随后,模块可以使用下面代码获得该内存的访问权:

dmabuf=ioremap(0XFF00000/**255M/, 0X100000/*1M/*);

解决方法之二是使用GPF_NOFAIL分配标志为缓冲区分配内存,但是该方法为内存管理子系统带来了相当大的压力。

解决方法之三十设备支持分散/聚集I/O,这可以将缓冲区分配成多个小块,设备会很好地处理它们。

 

4. 通用DMA层

DMA操作最终会分配缓冲区,并将总线地址传递给设备。内核提高了一个与总线——体系结构无关的DMA层。强烈建议在编写驱动程序时,为DMA操作使用该层。使用这些函数的头文件是。

int dma_set_mask(struct device *dev, u64 mask);

该掩码显示该设备能寻址能力对应的位。比如说,设备受限于24位寻址,则mask应该是0x0FFFFFF。

5. DMA映射

IOMMU在设备可访问的地址范围内规划了物理内存,使得物理上分散的缓冲区对设备来说成连续的。对IOMMU的运用需要使用到通用DMA层,而vir_to_bus函数不能完成这个任务。但是,x86平台没有对IOMMU的支持。

解决之道就是建立回弹缓冲区,然后,必要时会将数据写入或者读出回弹缓冲区。缺点是降低系统性能。

根据DMA缓冲区期望保留的时间长短,PCI代码区分两种类型的DMA映射:

一是一致性DMA映射,存在于驱动程序生命周期中,一致性映射的缓冲区必须可同时被CPU和外围设备访问。一致性映射必须保存在一致性缓存中。建立和使用一致性映射的开销是很大的。

二是流式DMA映射,内核开发者建议尽量使用流式映射,原因:一是在支持映射寄存器的系统中,每个DMA映射使用总线上的一个或多个映射寄存器,而一致性映射生命周期很长,长时间占用这些这些寄存器,甚至在不使用他们的时候也不释放所有权;二是在一些硬件中,流式映射可以被优化,但优化的方法对一致性映射无效。

6. 建立一致性映射

驱动程序可调用pci_alloc_consistent函数建立一致性映射:

void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int falg);

该函数处理了缓冲区的分配和映射,前两个参数是device结构和所需的缓冲区的大小。函数在两处返回DMA映射的结果:函数的返回值是缓冲区的内核虚拟地址,可以被驱动程序使用;而与其相关的总线地址保存在dma_handle中。

当不再需要缓冲区时,调用下函数:

void dma_free_conherent(struct device *dev, size_t size, void *vaddr, dma_addr_t *dma_handle);

7. DMA池

DMA池是一个生成小型,一致性DMA映射的机制。调用dma_alloc_coherent函数获得的映射,可能其最小大小为单个页。如果设备需要的DMA区域比这还小,就是用DMA池。在中定义了DMA池函数:

struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation);

void dma_pool_destroy(struct dma_pool *pool);

name是DMA池的名字,dev是device结构,size是从该池中分配的缓冲区的大小,align是该池分配操作所必须遵守的硬件对齐原则(用字节表示),如果allocation不为零,表示内存边界不能超越allocation。比如说传入的allocation是4K,表示从该池分配的缓冲区不能跨越4KB的界限。

在销毁之前必须向DMA池返回所有分配的内存。

void * dma_pool_alloc(sturct dma_pool *pool, int mem_flags, dma_addr_t *handle);

void dma_pool_free(struct dma_pool *pool, void *addr, dma_addr_t addr);

8. 建立流式DMA映射       

在某些体系结构中,流式映射也能够拥有多个不连续的页和多个“分散/聚集”缓冲区。建立流式映射时,必须告诉内核数据流动的方向。

DMA_TO_DEVICE

DEVICE_TO_DMA

如果数据被发送到设备,使用DMA_TO_DEVICE;而如果数据被发送到CPU,则使用DEVICE_TO_DMA。

DMA_BIDIRECTTONAL

如果数据可双向移动,则使用该值

DMA_NONE

该符号只是出于调试目的。

当只有一个缓冲区要被传输的时候,使用下函数映射它:

dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);

返回值是总线地址,可以把它传递给设备;如果执行错误,返回NULL。

当传输完毕后,使用下函数删除映射:

void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma-data_direction direction);

使用流式DMA的原则:

一是缓冲区只能用于这样的传送,即其传送方向匹配与映射时给定的方向值;

二是一旦缓冲区被映射,它将属于设备,不是处理器。直到缓冲区被撤销映射前,驱动程序不能以任何方式访问其中的内容。只用当dma_unmap_single函数被调用后,显示刷新处理器缓存中的数据,驱动程序才能安全访问其中的内容。

三是在DMA出于活动期间内,不能撤销对缓冲区的映射,否则会严重破坏系统的稳定性。             

如果要映射的缓冲区位于设备不能访问的内存区段(高端内存),怎么办?一些体系结构只产生一个错误,但是其他一些系统结构件创建一个回弹缓冲区。回弹缓冲区就是内存中的独立区域,它可被设备访问。如果使用DMA_TO_DEVICE标志映射缓冲区,并且需要使用回弹缓冲区,则在最初缓冲区中的内容作为映射操作的一部分被拷贝。很明显,在拷贝后,最初缓冲区内容的改变对设备不可见。同样DEVICE_TO_DMA回弹缓冲区被dma_unmap_single函数拷贝回最初的缓冲区中,也就是说,直到拷贝操作完成,来自设备的数据才可用。

有时候,驱动程序需要不经过撤销映射就访问流式DMA缓冲区的内容,为此内核提供了如下调用:

void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_directction direction);

应该在处理器访问流式DMA缓冲区前调用该函数。一旦调用了该函数,处理器将“拥有”DMA缓冲区,并可根据需要对它进行访问。然后在设备访问缓冲区前,应该调用下面的函数将所有权交还给设备:

void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);

再次强调,处理器在调用该函数后,不能再访问DMA缓冲区了。

DMA---直接存储器访问 DMA就是建立数据传输通道寄存器名称作用DMA_CCRxDMA通道x配置寄存器用于配置DMA(核心控制寄存器)DMA_ISRDMA中断状态寄存器用于查询当前DMA传输状态DMA_IFCRDMA中断标志清除寄存器用来清除DMA_ISR对应位DMA_CNDTRxDMA通道x传输数量寄存器用于控制DMA通道x每次传输的数据量DMA_CPARxDMA通道x外设地址寄存器用于存储STM32外设地址DMA_CMARxDMA通道x存储器地址寄存器用于存放存储器的地址。 阅读详情

相关推荐

直接内存访问DMA)算法

DMA控制器根据预先设置好的传输参数(如源地址、目的地址、传输长度等),直接内存和设备之间进行数据的搬移,期间不需要CPU频繁地参与每一个数据字节的传输操作。包括源地址寄存器(指定数据的来源地址,比如设备的数据缓冲区地址)、目的地址寄存器(指定数据要传输到的内存地址)、传输长度寄存器(指定要传输的数据字节数)等。请注意,以上代码只是为了演示DMA传输控制和缓冲区管理的基本原理和实现方式,在实际应用中,可能需要根据具体的硬件平台和系统环境进行更深入的调整和优化。• 首先将内存中的数据填充到DMA缓冲区。

qq_40844444的博客 1560

关于dma_alloc_coherent的用法

目录 概述 DMA映射 建立一致性DMA映射 DMA池 建立流式DMA映射 单页流式映射 分散/聚集映射 PCI双重地址周期映射 DAC 简单的PCI DMA例子 ISA设备DMA 概述 DMA操作是需要物理地址的,但是在linux内核中使用的都是虚拟地址,如果想要用DMA对一段内存进行操作,如何得到这一段内存的物理地址和虚拟地址的映射呢?dma_alloc_coherent这个函数实现了这种机制。 1、函数原型: void *dma_alloc_coherent( struct

六六哥的博客 1万+

Linux驱动:DMA直接内存访问

DMA(Direct Memory Aaccess)直接内存访问,意思是不通过CPU的干涉直接将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。它的作用就是解决大量数据传输过度消耗CPU资源的问题。DMA节省了大量的CPU资源,使CPU专注于更加实用的操作

qq_40709487的博客 1503

dma_alloc_coherent DMA内存申请学习笔记

dma_alloc_coherent 相关的知识网络上有很多,将相关的知识收集汇总以便于后续遇到问题方便查找。这一次对dma_alloc_coherent进行学习的原因是在使用rapidio驱动时申请大于64M的DMA内存时,遇到多次申请无法申请到内存的情况(加大预留DMA内存不能解决)。进而学习一下相关知识看是否有方法能解决相关问题。遇到的问题DMA无法申请大于32M的内存,MAX_ORDER 这个宏设置的太小,这个宏限制了一次请求所能分配的最大物理页数。如果申请5MB内存,MAX_ORDER 需要不小于

qq_42254853的博客 8670

linuxDMA( Direct Memory Access,直接内存访问

DMA是一种重要的硬件机制,能够让设备直接内存通信,减少 CPU 的参与,提高数据传输速度和系统整体性能。通过不同的工作模式(如突发模式、循环窃取模式等)和类型(内存内存、设备到内存等),DMA 能够适应不同的应用场景,广泛应用于磁盘 I/O、网络传输、多媒体处理等领域。

李白 2185

【实时Linux实战系列】实时直接内存访问DMA)实现技巧

本文介绍了实时Linux系统中直接内存访问(DMA)的实现方法。DMA技术允许硬件设备直接访问内存,绕过CPU,显著提升数据传输效率。文章重点阐述了三个核心概念:DMA工作原理、DMA控制器配置和缓存一致性处理,并针对常见问题提供了具体解决方案,包括DMA控制器配置代码示例、数据对齐优化和中断处理策略。同时给出了实践建议,如选择合适传输模式、调整中断策略等。通过合理应用这些方法,可以优化实时控制系统的性能和可靠性,为开发者提供实用的技术参考。

望获实时Linux系统 815

Linux内核中的DMA管理:直接内存访问机制详解

DMALinux内核中实现高性能数据传输的关键机制,它通过减少CPU干预,提高了系统的整体性能。作为嵌入式开发者,理解DMA的工作原理和使用方法,对于开发高性能的设备驱动至关重要。

jiang_style的博客 2009

30、深入理解Linux内核内存分配与直接内存访问DMA

本文深入探讨了Linux内核中的内存分配机制与直接内存访问DMA)技术,详细解析了内存映射、DMA工作原理及性能特点。文章涵盖了DMA映射的两种主要类型:一致性DMA映射和流式DMA映射,并通过代码示例说明了如何在设备驱动中实现和使用DMA。此外,还介绍了DMA引擎的API、循环DMA配置、设备树(DT)绑定等内容,为开发高效、高性能的Linux设备驱动程序提供了全面的技术指导。

ik6789012的博客 55

30、Linux内核内存分配与直接内存访问DMA)支持详解

本文详细探讨了Linux内核中的内存分配机制和直接内存访问DMA)的支持。文章首先回顾了内存映射的基础知识,接着深入解析了DMA的工作原理、性能特点以及相关的缓存一致性问题。文中还介绍了DMA映射的设置方法,包括一致性DMA映射和流式DMA映射的使用场景及实现细节。此外,还涵盖了DMA完成机制、DMA引擎API的使用、单缓冲区和循环DMA的实现,以及DMA与设备树(DT)的绑定等内容。通过本文,读者可以全面了解DMA技术的核心概念及其在Linux内核中的具体应用。

ik678901234的博客 72

Linux驱动开发基础之DMA

本文对于DMALinux驱动中的相关运用进行了简单的介绍,并且也提到了一些DMA的相关概念以函数。对于DMA的传输过程进行了详细的介绍,并通过相关的代码进行了实现。下文所使用的硬件设备是基于ARM Cortex-A9处理器的Exynos-tiny4412开发板所实现的,所使用的内核版本为Linux-3.5提示:以下是本篇文章正文内容,下面案例可供参考。

ZZZZZ1113的博客 4829

Linux内核中DMA分析

 DMA---直接内存访问 用来在设备内存与主存RAM之间直接进行数据交换,这个过程无需CPU干预, 对于系统中有大量数据交换的设备而言,如果能够充分利用DMA特性,可以大大提高系统性能。 1.内核中DMA层 --内核为设备驱动程序提供了统一的DMA接口,这些接口屏蔽了不同平台之间的差异。 --一致性映射类型的dma_alloc_coherent/流式映射类型的dma_map_si

Jun's blog 7677

linux驱动-DMA

DMA概述: DMA就是与高速总线直接通信。一般外设,都是通过低速总线与CPU通信。 DMA映射,就是通过一些设置后,外设部设备可以通过高速总线与CPU通信。具体来说,RAM是通过高速总线与CPU通信,只要建立起外部设备能够存取RAM空间,CPU是能直接存取RAM,这样就外部设备和CPU之间通过RAM建立了通信,这就是映射过程。 DMA数据传输概况: DMA数据传输两种情况: 软件请

子木 4809

DMA内存申请--dma_alloc_coherent 及 寄存器与内存

在项目驱动过程中会经常用到dma传输数据,而dma需要的内存有自己的特点,一般认为需要物理地址连续,并且内存是不可cache的,在linux内核中提供一个供dma所需内存的申请函数dma_alloc_coherent. 如下所述: dma_alloc_coherent() dma_alloc_coherent() -- 获取物理页,并将该物理页的总线地址保存于dma_handle,返回该物理页的虚拟地址 DMA映射建立了一个新的结构类型---------dma_addr_t来表示总线地址。dma_a.

乐于分享 7108

dma_alloc_coherent 申请内存用法和问题总结

检查内核打印,给cma分配的地址是否在4G以上,dma_allocat_coherent() 要求地址低于掩码[(0x1 << 32)-1] = 0xFFFFFFFF。4M这个限制是本身应该是分的普通内存的限制, 例如把配置加一个CONFIG_FORCE_MAX_ZONEORDER=16把最大限制改成128M也能解决。使用这个接口最大只能申请到4M的内存,申请超过4M时,报。在dts中添加cma节点,可指定cma大小和空间。,dma分配默认会走cma,空间会大点。可配置大小,也可指定地址。

kunkliu的博客 8587

dma_alloc_coherent (建立一致性 DMA 映射函数)

A: 内存的虚拟起始地址,在内核要用此地址来操作所分配的内存 B: struct device指针,可以平台初始化里指定,主要是dma_mask之类,可参考framebuffer C: 实际分配大小,传入dma_map_size即可 D: 返回的内存物理地址,dma就可以用。 所以,A和D是一一对应的,只不过,A是虚拟地址,而D是物理地址。对 任意一个操作都将改变缓冲区内容。当然要注意操作环境。

ZP1015 2万+

DMA实践4:使用dma_alloc_coherent替代dma_map_single

DMA实践4:使用dma_alloc_coherent替代dma_map_single

4204

linux中关于dma_alloc_coherent的用法

大家都知道,DMA操作是需要物理地址的,但是在linux内核中使用的都是虚拟地址,如果我们想要用DMA对一段内存进行操作,我们如何得到这一段内存的物理地址和虚拟地址的映射呢?dma_alloc_coherent这个函数实现了这种机制。 1、函数原型:void *dma_alloc_coherent(struct device *dev, size_t size,dma_addr_t *d

强强的专栏 5万+
上一篇: linux中的物理地址,虚拟地址,总线地址的区别
下一篇: Makefile使用总结
fzjcycp
博客等级 码龄12年 73粉丝 52原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值