Linux DMA meory简述

本文详细解释了DMA内存的分类、操作函数及其在MIPS和ARM平台的具体实现,包括coherent和non-coherent内存的区别,DMA-API与PCI-API的差异,以及在不同体系结构下的内存映射与同步机制。
部署运行你感兴趣的模型镜像
首先,应该先读Documentation/DMA-API.txtDMA-mapping.txt.

1.       DMA memeory分类

一共有两种DMA memory

a.       Consistent/coherent

b.      non-consistent/no-coherent

第一类由底层硬件保证了内存的一致性,这样的内存当进行DMA时,不需要相关的invalidate函数操作。

第二类内存在进行DMA之前,必须做flush/invalidate等操作。

底层硬件如何实现,或者处理器通过内存映射,或者有额外的硬件支持,这些对于driver来说是透明的。

注意 coherentno-coherentcache是不同的概念,coherent也许是cached,也许是uncached,这是由底层硬件来实现。

 

2.       DMAmemory的操作函数

DMA-API有两类,

第一类是dma_ API, 它必须包含#include <linux/dma-mapping.h>

第二类是pci_ API, 它必须包含#include <linux/pci.h>

第一类是向处理器直接分配内存,第二类是通过pci的接口分配内存。对于mipsarm来说,这两类API是一样的,pci_ APImipsarm处理器中最后会走到dma_ API

所以这里只提及DMA-API

 

dma_alloc_coherent

分配coherent内存。

一般来说,coherent内存是比较昂贵,或者代价比较高一些,所以只能小块的内存使用coherent,比如典型的descriptor

 

dma_pool_create

这个用来分配一个小块coherent的内存池子。dma_alloc_coherent是以页为单位分配。

 

dma_sync_single_for_device

dma_sync_single_for_cpu


并不太确定这两个sync是啥意思,很重要的两个函数,在mips上的实现也不同,一个是做了flush的动作,一个是没做

 

dma_map_single

flush这段内存,并返回物理地址,当分配buffer的时候,通常会使用这个函数返回物理地址。

3.       DMA的层次结构

3.1   对于DMA_ API来说,从<linux/dma-mapping.h>开始,这里定义了一些公共的API,然后在这个头文件中会

#ifdef CONFIG_HAS_DMA

#include <asm/dma-mapping.h>

#else

#include <asm-generic/dma-mapping-broken.h>

#endif

一般我们会定义CONFIG_HAS_DMA,所以会包含体系相关dma-mapping.h

对于mips来说,是arch/mips/include/asm/dma-mapping.h

对于arm来说,是arch/arm/include/asm/dma-mapping.h


在这两个头文件中,可以直接调用到各个arch中的dma mapping的实现,也可以在封装一层,

再包含#include dma-maping-common.h,然后这里通过dma_map_ops函数指针调到各个arch的dma mapping实现。

 

体系相关的dma-mapping.h中声明了其它DMA_API。它在哪里实现了,

对于mips来说,它在arch/mips/mm/dma-default.c

对于arm来说,它在arch/arm/mm/dma-mapping.c

 

对于PCI_ API来说,从<linux/pci.h>开始。

这个头文件会包含体系相关的<asm/pci.h>

对于mips来说,是arch/mips/include/asm/pci.h

对于arm来说,是arch/arm/include/asm/pci.h

对于mipsarm来说,pci.h又会包含如下语句

/* implement the pci_ DMA API in terms of the generic device dma_ one */

#include <asm-generic/pci-dma-compat.h>

 

然后在pci-dma-compat.h里,开始包含#include <linux/dma-mapping.h>,并且把PCI_DMA_做了转换,这就把PCI_DMA_联系上了。

 

4.       MIPS上的DMA实现

dma-default.c中实现了mipsDMA函数

dma_alloc_coherent

        void *ret;

 

        gfp = massage_gfp_flags(dev, gfp);

 

        ret = (void *) __get_free_pages(gfp, get_order(size));

 

        if (ret) {

                        memset(ret, 0, size);

                        *dma_handle = plat_map_dma_mem(dev, ret, size);

 

                        if (!plat_device_is_coherent(dev)) {

                                        dma_cache_wback_inv((unsigned long) ret, size);

                                        ret = UNCAC_ADDR(ret);

                        }

        }

 

        return ret;

}

从这个函数可以看出,mips分配一个页面,清空,返回物理地址,然后判断这个device是否是coherent,因为我们前面说过可以有额外的硬件来支持coherent

plat_device_is_coherent

{

#ifdef CONFIG_DMA_COHERENT

        return 1;

#endif

#ifdef CONFIG_DMA_NONCOHERENT

        return 0;

#endif

}

对于mips来讲,它依据CONFIG_DMA_COHERENTCONFIG_DMA_NONCOHERENT

定义。它只是提供了这样一个函数给所有的平台。

一般我们会定义CONFIG_DMA_NONCOHERENT

 

所以这里,mips是如何操作这coherent函数的呢?它把这一页先write-inv, 然后返回SEG1地址,其实就是uncache,这样确实保证了coherent,但效率会变低。

dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,

        enum dma_data_direction direction)

{

        unsigned long addr = (unsigned long) ptr;

 

        if (!plat_device_is_coherent(dev))

                        __dma_sync(addr, size, direction);

 

        return plat_map_dma_mem(dev, ptr, size);

}

Dma_map_singlesync的作用,并且返回物理地址。

static inline void __dma_sync(unsigned long addr, size_t size,

        enum dma_data_direction direction)

{

        switch (direction) {

        case DMA_TO_DEVICE:

                        dma_cache_wback(addr, size);

                        break;

 

        case DMA_FROM_DEVICE:

                        dma_cache_inv(addr, size);

                        break;

 

        case DMA_BIDIRECTIONAL:

                        dma_cache_wback_inv(addr, size);

                        break;

 

        default:

                        BUG();

        }

}

对于写device,我们需要wback,对于从device读,我们需要invalidate,对于双向,我们需要invalidate+wback

这个涉及到cache的操作了,具体如何操作,与cache的类型相关。

arch/mips/include/asm/io.h

#ifdef CONFIG_DMA_NONCOHERENT

#define dma_cache_wback_inv(start, size) _dma_cache_wback_inv(start, size)

#define dma_cache_wback(start, size)                         _dma_cache_wback(start, size)

#define dma_cache_inv(start, size)                                _dma_cache_inv(start, size)

 

_dma_cache_wback_inv这些底层函数的赋值则是在系统起来,初始化cache时赋值的,mips有多种类型,对于24K,74K等,它会使用原先的R4k cache,这里我们要选择CONFIG_CSRC_R4K=y

r4k_cache_init函数中

                        _dma_cache_wback_inv              = r4k_dma_cache_wback_inv;

                        _dma_cache_wback        = r4k_dma_cache_wback_inv;

                        _dma_cache_inv                           = r4k_dma_cache_inv;

 

所以最后DMA的操作到了r4kcache操作上来。

5.       ARM上的DMA实现

因为不太懂ARM,这里只说一下arm cache的层次结构。

一般如果要sync一个bufferarm提供到外面的接口有

___dma_single_cpu_to_dev

___dma_single_dev_to_cpu

我不知道这两个的区别,这个要留到以后分析。

 

___dma_single_dev_to_cpu

{

                BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1));

 

                /* FIXME: non-speculating: not required */

                /* don't bother invalidating if DMA to device */

                if (dir != DMA_TO_DEVICE) {

                                unsigned long paddr = __pa(kaddr);

                                outer_inv_range(paddr, paddr + size);

                }

 

                dmac_unmap_area(kaddr, size, dir);

}

 

define dmac_unmap_area                                          cpu_cache.dma_unmap_area

 

如果有需要还要使用outer_inv_rangeinvalidate L2cache

然后需要dmac_unmap_area

 

这个函数在cacheflush.h中有定义,表示为cpu_cache.dma_unmap_area,这个文件中还定义 了很多其它cache的操作,因为arm有不同的型号,所以每一种 cache都不一样。

arch/arm/mm下面,cpu_cache在如下函数中定义

如果CPUARMV7的话,则在cache-v7.S最后两句,定义了这个结构

        @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)

        define_cache_functions v7

 

define_cache_functions  v7则是在proc-macros.S中定义

ENTRY(\name\()_cache_fns)

        .long   \name\()_flush_icache_all

其实就是 v7_flush_icache_all等。

 

v7_flush_icache_all这些函数的定义 则是在cache-v7.S

 

ENTRY(v7_dma_unmap_area)

        add     r1, r1, r0

        teq     r2, #DMA_TO_DEVICE

        bne     v7_dma_inv_range

        mov     pc, lr

ENDPROC(v7_dma_unmap_area)

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.9

TensorFlow-v2.9

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

在 ABAP 中,`MODIFY` 和 `MEMORY ID` 是两个完全不同的概念,分别用于不同的目的。下面详细解释它们的区别。 --- ### 1. `MODIFY` 语句 `MODIFY` 是 ABAP 中用于**修改数据库表中的记录**的语句。它将数据从 ABAP 程序的内部表或工作区更新到数据库表中。 #### 示例代码: ```abap DATA: ls_ekko TYPE ekko. SELECT SINGLE * FROM ekko INTO ls_ekko WHERE ebeln = '1000000001'. ls_ekko-zzabc = 'X'. " 修改某个字段 MODIFY ekko FROM ls_ekko. ``` #### 说明: - 如果 `ls_ekko` 中的主键(如 `ebeln`)已经在数据库表中存在,则 `MODIFY` 会更新该记录。 - 如果不存在,则会插入一条新记录(取决于表的设置和上下文)。 - `MODIFY` 是 DML(数据操作语言)语句,直接作用于数据库。 --- ### 2. `MEMORY ID`(内存 ID) `MEMORY ID` 是用于在程序之间**共享数据**的一种机制,它允许你将数据存储在 SAP 内存中,并通过一个 ID 标识符在不同程序之间传递数据。 #### 示例代码: **程序 A:设置内存 ID** ```abap DATA: lv_value TYPE string VALUE 'Hello from Program A'. SET PARAMETER ID 'ABC' FIELD lv_value. ``` **程序 B:读取内存 ID** ```abap DATA: lv_value TYPE string. GET PARAMETER ID 'ABC' INTO lv_value. WRITE: / 'Value from memory:', lv_value. ``` #### 说明: - `SET PARAMETER ID` 和 `GET PARAMETER ID` 用于设置和读取 SAP 内存中的数据。 - `MEMORY ID` 的作用范围是整个 SAP 会话(session),而不是单个程序。 - 它常用于屏幕之间、程序之间传递参数。 --- ### 总结区别 | 特性 | `MODIFY` | `MEMORY ID` | |------------------|-----------------------------------|-------------------------------------| | 目的 | 修改数据库表中的记录 | 在程序之间共享数据 | | 操作对象 | 数据库表 | SAP 内存 | | 是否持久化 | 是(影响数据库) | 否(仅存在于会话期间) | | 语法 | `MODIFY table FROM wa.` | `SET PARAMETER ID` / `GET PARAMETER ID` | | 使用场景 | 更新数据库记录 | 跨程序/屏幕传递数据 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值