原创 宋宝华 Linux阅码场 2018-01-22html
做者简介
宋宝华,他有10几年的Linux开发经验。他长期在大型企业担任一线工程师和系统架构师,编写大量的Linux代码,并负责在gerrit上review其余同事的代码。Barry Song是Linux的活跃开发者,是某些内核版本的最活跃开发者之一(如https://lwn.net/Articles/395961/ 、https://lwn.net/Articles/429912/),也曾是一ARM SoC系列在Linux mainline的maintainer。他也是china-pub等评估的2008年度“十大畅销经典”,“十佳原创精品”图书《Linux设备驱动开发详解》的做者和《Essential Linux Device Driver》的译者。同时书写了不少技术文章,是51CTO 2012年度“十大杰出IT博客”得主及51CTO、CSDN的专家博主。他也热衷于开源项目,正在开发LEP(Linux Easy Profiling,http://www.linuxep.com/)项目,并但愿得到更多人的参与和帮助。前端
本文目录
1.DMA ZONE的大小是16MB?
2.DMA ZONE的内存只能作DMA吗?
3.dma_alloc_coherent()申请的内存来自DMA ZONE?
4.dma_alloc_coherent()申请的内存是非cache的吗?
5.dma_alloc_coherent()申请的内存必定是物理连续的吗?linux
这个答案在32位X86计算机的条件下是成立的,可是在其余的绝大多数状况下都不成立。
首先咱们要理解DMA ZONE产生的历史缘由是什么。DMA能够直接在内存和外设之间进行数据搬移,对于内存的存取来说,它和CPU同样,是一个访问master,能够直接访问内存。
DMA ZONE产生的本质缘由是:不必定全部的DMA均可以访问到全部的内存,这本质上是硬件的设计限制。git
在32位X86计算机的条件下,ISA实际只能够访问16MB如下的内存。那么ISA上面假设有个网卡,要DMA,超过16MB以上的内存,它根本就访问不到。因此Linux内核干脆简单一点,把16MB砍一刀,这一刀如下的内存单独管理。若是ISA的驱动要申请DMA buffer,你带一个GFP_DMA标记来代表你想从这个区域申请,我保证申请的内存你是能够访问的。后端
DMA ZONE的大小,以及DMA ZONE要不要存在,都取决于你实际的硬件是什么。好比我在CSR工做的时候,CSR的primaII芯片,尽管除SD MMC控制器之外的全部的DMA均可以访问整个4GB内存,但MMC控制器的DMA只能访问256MB,咱们就把primaII对应Linux的DMA ZONE设为了256MB,详见内核:arch/arm/mach-prima2/common.c架构
#ifdef CONFIG_ARCH_PRIMA2 static const char *const prima2_dt_match[] __initconst = { "sirf,prima2", NULL }; DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)") /* Maintainer: Barry Song <baohua.song@csr.com> */ .l2c_aux_val = 0, .l2c_aux_mask = ~0, .dma_zone_size = SZ_256M, .init_late = sirfsoc_init_late, .dt_compat = prima2_dt_match, MACHINE_END #endif
不过CSR这个公司因为早前已经被Q记收购,已经再也不存在,一块儿幻灭的,还有当年挂在汽车前窗上的导航仪。这不由让我想起咱们当年在ADI arch/blackfin里面写的代码,也渐渐快几乎没有人用了同样。
一代人的芳华已逝,面目全非,重逢虽然谈笑如故,可不难看出岁月给每一个人带来的改变。原谅我不肯让大家看到咱们老去的样子,就让代码,留住咱们芬芳的年华吧........app
下面咱们架空历史,假设有一个以下的芯片,里面有5个DMA,A、B、C均可以访问全部内存,D只能访问32MB,而E只能访问64MB,你以为Linux的设计者会把DMA ZONE设置为多大?固然是32MB,由于若是设置为64MB,D从DMA ZONE申请的内存就可能位于32MB-64MB之间,申请了它也访问不了。
因为现现在绝大多少的SoC都很牛逼,彷佛DMA都没有什么缺陷了,根本就不太可能给咱们机会指定DMA ZONE大小装逼了,那个这个ZONE就不太须要存在了。反正任何DMA在任何地方申请的内存,这个DMA均可以存取到。ide
DMA ZONE的内存作什么均可以。DMA ZONE的做用是让有缺陷的DMA对应的外设驱动申请DMA buffer的时候从这个区域申请而已,可是它不是专有的。其余全部人的内存(包括应用程序和内核)也能够来自这个区域。.net
dma_alloc_coherent()申请的内存来自于哪里,不是由于它的名字前面带了个dma_就来自DMA ZONE的,本质上取决于对应的DMA硬件是谁。看代码:设计
static void *_ _dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, pgprot_t prot, bool is_coherent, const void *caller) { u64 mask = get_coherent_dma_mask(dev); … if (mask < 0xffffffffULL) gfp |= GFP_DMA; … }
对于primaII而言,绝大多少的外设的dma_coherent_mask都设置为0XffffffffULL(4GB内存全覆盖),可是SD那个则设置为256MB-1对应的数字。这样当primaII的SD驱动调用dma_alloc_coherent()的时候,GFP_DMA标记被设置,以指挥内核从DMA ZONE申请内存。可是,其余的外设,mask覆盖了整个4GB,调用dma_alloc_coherent()得到的内存就不须要必定是来自DMA ZONE。
要解答这个问题,首先要理解什么叫cache coherent。仍是继续看这个DMA的图,咱们假设MEM里面有一块红色的区域,而且CPU读过它,因而红色区域也进CACHE:
可是,假设如今DMA把外设的一个白色搬移到了内存本来红色的位置:
这个时候,内存虽然白了,CPU读到的却仍是红色,由于CACHE命中了,这就出现了cache的不coherent。
固然,若是是CPU写数据到内存,它也只是先写进cache(不必定进了内存),这个时候若是作一个内存到外设的DMA操做,外设可能就获得错误的内存里面的老数据。
因此cache coherent的最简单方法,天然是让CPU访问DMA buffer的时候也不带cache。事实上,缺省状况下,dma_alloc_coherent()申请的内存缺省是进行uncache配置的。
可是,因为现代SoC特别强,这样有一些SoC里面能够用硬件作CPU和外设的cache coherence,如图中的cache coherent interconnect:
这些SoC的厂商就能够把内核的通用实现overwrite掉,变成dma_alloc_coherent()申请的内存也是能够带cache的。这部分仍是让大牛Arnd Bergmann童鞋来解释:
来自:https://www.spinics.net/lists/arm-kernel/msg322447.html
Arnd Bergmann:
dma_alloc_coherent() is a wrapper around a device-specific allocator,
based on the dma_map_ops implementation. The default allocator
from arm_dma_ops gives you uncached, buffered memory. It is expected
that the driver uses a barrier (which is implied by readl/writel
but not __raw_readl/__raw_writel or readl_relaxed/writel_relaxed)
to ensure the write buffers are flushed.
If the machine sets arm_coherent_dma_ops rather than arm_dma_ops,
the memory will be cacheable, as it's assumed that the hardware
is set up for cache-coherent DMAs.
当我grep内核源代码的时候,我发现部分SoC确实是这样实现的:
baohua@baohua-VirtualBox:~/develop/linux/arch/arm$ git grep arm_coherent_dma_ops
include/asm/dma-mapping.h:extern struct dma_map_ops arm_coherent_dma_ops;
mach-highbank/highbank.c: set_dma_ops(dev, &arm_coherent_dma_ops);
mach-mvebu/coherency.c: set_dma_ops(dev, &arm_coherent_dma_ops);
绝大多数的SoC目前都支持和使用CMA技术,而且多数状况下,DMA coherent APIs以CMA区域为申请的后端,这个时候,dma alloc coherent本质上用_ _alloc_from_contiguous()从CMA区域获取内存,申请出来的内存显然是物理连续的。这一点,在设备树dts里面就能够轻松配置,要么配置一个本身特定的cma区域,要么从“linux,cma-default”指定的缺省的CMA池子里面取内存:
reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; /* global autoconfigured region for contiguous allocations */ linux,cma { compatible = "shared-dma-pool"; reusable; size = <0x4000000>; alignment = <0x2000>; linux,cma-default; }; display_reserved: framebuffer@78000000 { reg = <0x78000000 0x800000>; }; multimedia_reserved: multimedia@77000000 { compatible = "acme,multimedia-memory"; reg = <0x77000000 0x4000000>; }; };
可是,若是IOMMU存在(ARM里面叫SMMU)的话,DMA彻底能够访问非连续的内存,而且把物理上不连续的内存,用IOMMU进行从新映射为I/O virtual address (IOVA):
因此dma_alloc_coherent()这个API只是一个前端的界面,它的内存究竟从哪里来,究竟要不要连续,带不带cache,都彻底是因人而异的。
最后总结一句,千万不要被教科书和各类网上的资料懵逼了双眼,你必定要真正本身探索和搞清楚事情的本源。今天看了《芳华》这部电影,感慨良多,遂做此文。