dma的大小
从开机开始看起 bootmem_init --- dma_contiguous_reserve(arm64_dma_phys_limit):
    if (size_cmdline != -1) {
这里是cmdline中使用cma=来指定起始地址和长度,比较自由任意
        selected_size = size_cmdline;
        selected_base = base_cmdline;
        selected_limit = min_not_zero(limit_cmdline, limit);
        if (base_cmdline + size_cmdline == limit_cmdline)
            fixed = true;
    } else {
这里则是听命系统分配了
#ifdef CONFIG_CMA_SIZE_SEL_MBYTES
        selected_size = size_bytes;
#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE)
        selected_size = cma_early_percent_memory();
#elif defined(CONFIG_CMA_SIZE_SEL_MIN)
        selected_size = min(size_bytes, cma_early_percent_memory());
#elif defined(CONFIG_CMA_SIZE_SEL_MAX)
        selected_size = max(size_bytes, cma_early_percent_memory());
#endif
    }
默认 CONFIG_CMA_SIZE_SEL_MBYTES,为16M
起始地址 base=__memblock_find_range_top_down(start, end, size, align, nid, flags)
从memblock的top开始往下找一块size (16M)大小的 memory属性(非reserved)的物理块.
注意了,开始找的地方是从dma的边界往下(arm64_dma_phys_limit)。
这块物理块会记录在 cma_areas[cma_area_count] 中,然后 dma_contiguous_default_area 指向这块物理块。
看来 cma 才是大哥,dma是小小弟啊。

获取dma内存的gfp mask
dma_direct_optimal_gfp_mask:
    if (*phys_limit <= DMA_BIT_MASK(zone_dma_bits))
        return GFP_DMA;
    if (*phys_limit <= DMA_BIT_MASK(32))
        return GFP_DMA32;
不同的mask,对应从不同的zone获取内存。若内存不足,往上一级借(steal)。这里优先是dma zone
拿ARM64举例:ARM64中高版本默认开启了dma和dma32 zone
在zone_sizes_init函数中,定义了这两个dma zone 的边界。
dma32边界是32bit,dma边界是不超过32bit,具体多小可以由固件定义,固件不定义,默认dma边界为32bit。
注意了,这里的边界就是实际的内存物理地址区间中的32bit地址处,内存物理地址起始高于32bit,dma zone就没了

获取dma内存
一致dma映射
dma_alloc_coherent
从dma_contiguous_default_area记录的区域中获取内存
cma_alloc_aligned(dma_contiguous_default_area, size, gfp);
然后调用到alloc_contig_range,gfp变成了GFP_KERNEL
pfn = cma->base_pfn + (bitmap_no << cma->order_per_bit);    // 空闲pfn
ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA,
                     GFP_KERNEL | (no_warn ? __GFP_NOWARN : 0))
page = pfn_to_page(pfn)
分配dma/cma有一个核心流程alloc_contig_range,检查该pfn是不是正在被伙伴系统使用,如果是则要腾出来
归还给dma/cma用。通常dma zone作为最贵重的zone,除非内存不足或者执意有人用了GFP_DMA占用了预留的dma,还有
就是使用cma=放飞了自我。

可以发现,从dma_contiguous_default_area中申请page,和gfp mask没有任何关系。
那啥情况和gfp有关呢。
1 config DMA_CMA没有开,则优先从dma zone获取page。
2 dma_contiguous_default_area 没货了,则优先从dma zone获取page
__dma_direct_alloc_pages:
    if (!page)
        page = alloc_pages_node(node, gfp, get_order(size));

一致性dma映射,可支持重新映射虚拟地址到vmalloc空间
    if (remap) {
    //ret为vmalloc空间
        ret = dma_common_contiguous_remap(page, size, prot,
                __builtin_return_address(0));    
    } else {
    // ret为线性映射空间
        ret = page_address(page);
    }
函数的返回值为申请到的物理页的虚拟地址,dma物理地址存放在参数中
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));

流式dma映射
#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, 0)
参数二:参与dma传输的虚拟地址buf
参数四:数据方向,系统会根据direction的值invalid 或者write back cache
返回的是dma地址
DMA_TO_DEVICE
urb->setup_dma = dma_map_single(
                    hcd->self.sysdev,
                    urb->setup_packet,                    //kmalloc
                    sizeof(struct usb_ctrlrequest),
                    DMA_TO_DEVICE);
DMA_FROM_DEVICE
urb->transfer_dma = dma_map_single(
                        hcd->self.sysdev,
                        urb->transfer_buffer,            //kmalloc
                        urb->transfer_buffer_length,
                        DMA_FROM_DEVICE);
函数定义为
static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
        size_t size, enum dma_data_direction dir, unsigned long attrs) {
    return dma_map_page_attrs(dev, virt_to_page(ptr), offset_in_page(ptr),
            size, dir, attrs)
}
这里最终会将ptr定位到一个具体的物理地址
phys = page_to_phys(virt_to_page(ptr)) + offset_in_page(ptr)
最后将64为的物理地址对应到32位的dma地址,phys_attr_t转化位dma_attr_t
dma_addr_t dma_addr = phys_to_dma(dev, phys)

对比一致性dma和流式dma:
一致性dma是从dma zone或者预留的dma/cma空间中获取
流式dma,使用kmalloc获取物理小内存,再转为dma地址

杂谈 linux DMA内存相关推荐

  1. Linux DMA 内存拷贝与memcpy 速率比较

    DMA理论:https://blog.csdn.net/yizhiniu_xuyw/article/details/113809126 驱动层代码: #include <linux/kernel ...

  2. linux内核dma内存分配,Linux 4.x 内核空间 DMA 虚拟内存地址

    Architecture: i386 32bit Machine Ubuntu 16.04 Linux version: 4.15.0-39-generic 目录 DMA 虚拟内存区 在 IA32 体 ...

  3. Linux DMA 驱动学习总结

    Linux DMA驱动构架分析 以linux2.6.32中的S3C2440驱动为例进行分析,DMA驱动所对应的源码为linux-2.6.32.2\arch \arm\mach-s3c2440\dma. ...

  4. linux 内核参数 rss,Linux控制内存的内核参数

    环境 Red Hat Enterprise Linux (RHEL) 5.x (X86) 在 X86 高内存设备中,当用户进程使用 mlock() 在常规区域分配大量内存时,可重新使用的 lowmem ...

  5. 深度好文:Linux操作系统内存

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 后台回复"k8s",可领取k8s资料 Linux 内存 ...

  6. linux查看内存_嵌入式操作系统的内存,你了解多少?

    关注.星标公众号,不错过精彩内容 来源:EDN电子技术设计 linux 内存是后台开发人员,需要深入了解的计算机资源.合理的使用内存,有助于提升机器的性能和稳定性.本文主要介绍 linux 内存组织结 ...

  7. Linux内核内存管理(3):kmemcheck介绍

    Linux内核内存管理 kmemcheck介绍 rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. 5.10.13不存在kmemcheck的概念,取代的是k ...

  8. [译] LINUX内核内存屏障

    ================= LINUX内核内存屏障 ================= By: David Howells <dhowells@redhat.com> Paul E ...

  9. Linux内核内存管理(2):固定映射地址(fixmap)和输入输出重映射(ioremap)

    Linux内核内存管理 固定映射地址(fixmap)和输入输出重映射(ioremap) rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. Print ke ...

最新文章

  1. 学霸女孩放弃保研再次高考,原因让人泪目…
  2. gdb 查看 stl容器 zz
  3. python读取文件_python这么受欢迎,你知道如何以正确的方式来读取文件内容吗
  4. php mail ld preload,读《利用环境变量LD_PRELOAD来绕过php disable_function执行系统命令》有感...
  5. DBDesigner 4 与 MySql 5 不能连接主要是驱动的原因
  6. 参加工作第三个月的感悟
  7. 【动态规划】P1048 01背包问题:采药
  8. DS4300电池即将过期,磁阵目前读写缓慢解决过程.txt
  9. GeoGebra官方版下载
  10. 绝密计划:我在阿里打黑工
  11. Android Canvas画布的详解与使用,以及View的绘画(一)
  12. 集成学习(二)——Bagging
  13. 云剪智能混剪软件/批量剪辑工具技术源码框架---- 一键生成上亿条原创视频
  14. java编程之拼图_Java编程制作拼图游戏
  15. 东昂科技冲刺深交所:年营收2.6亿 庄俊辉控制69%股权
  16. ZZULIOJ【1088】手机短号【输入输出格式】
  17. jpg怎么转换doc
  18. 每日一道leetcode(python)46. 全排列
  19. 简述汇编语言中的标号有什么规定_汇编语言期末复习题
  20. 百度推广计划改为计算机端,百度推广怎么样搭建一个优秀的推广计划

热门文章

  1. Python中的字典到底是有序的吗
  2. 用JAVA实现简单的绘画操作
  3. ADworld pwn wp - play
  4. 学习笔记 | 树的最近公共祖先
  5. 常用的程序调试方法(C/C++)
  6. “一盘货卖全球”之后,天猫今年将推出国货“出海2.0版”
  7. 高中计算机教学评价,信息技术课课堂教学评价表.doc
  8. mysql实现排名函数三种方式
  9. 怎么用ps存Ae的html,怎么把ps文件导入到ae
  10. 退不了款的ofo,不能了的资本局 | 一点财经