原文:scatterlist && DMA

DMA是一种无须CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制。使用DMA可以是系统CPU从实际的IO数据传输过程中摆脱出来,从而大大提

供系统的吞吐率。DMA方式的数据传输由DMA控制器(DMAC)控制,在传输期间,CPU可以并发地执行其他任务,当DMA结束后,DMAC通过中断通知CPU数据传输已经结束,然后由CPU执行相应的中断服务程序进行后续处理。

在内存中用于与外设交互数据的一块区域被称作DMA缓冲区,在设备不支持scatter/gatherCSG,分散/聚集操作的情况下,DMA缓冲区必须是物理上联系的。

对于ISA设备而言,其DMA操作只能在16MB以下的内存进行,因此,在使用kmalloc()和__get_free_pages()及其类似函数申请DMA缓冲区时应使用GFP_DMA标志,这样能保证获得的内存是具备DMA能力的。

DMA的硬件使用总线地址而非物理地址,总线地址是从设备角度上看到的内存地址,物理地址是从CPU角度上看到的未经转换的内存地址(经过转换的那叫虚拟地址)。

在PC上,对于ISA和PCI而言,总线即为物理地址,但并非每个平台都是如此。由于有时候接口总线是通过桥接电路被连接,桥接电路会将IO地址映射为不同的物理地址。

设备不一定能在所有的内存地址上执行DMA操作,在这种情况下应该通过下列函数执行DMA地址掩码:

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

DMA映射包括两个方面的工作:

  • 分配一片DMA缓冲区;
  • 为这片缓冲区产生设备可访问的地址。

内核中提供了一下函数用于分配一个DMA一致性的内存区域:

  1. void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);

这个函数的返回值为申请到的DMA缓冲区的虚拟地址。此外,该函数还通过参数handle返回DMA缓冲区的总线地址。与之对应的释放函数为:

  1. void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);

以下函数用于分配一个写合并(writecombinbing)的DMA缓冲区:

  1. void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);

与之对应的是释放函数:dma_free_writecombine(),它其实就是dma_free_conherent,只不过是用了#define重命名而已。

  1. 对于单个已经分配的缓冲区而言,使用dma_map_single()可实现流式DMA映射:
  1. dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);
  2. 如果映射成功,返回的是总线地址,否则返回NULL.最后一个参数DMA的方向,可能取DMA_TO_DEVICE, DMA_FORM_DEVICE, DMA_BIDIRECTIONALDMA_NONE;
  1. 与之对应的反函数是:
  1. void dma_unmap_single(struct device *dev,dma_addr_t *dma_addrp,size_t size,enum dma_data_direction direction);

MMC的scatter list相关操作

MMC作为块设备,它的存储空间,最小单位由struct bio_vec 描述,它代表一段物理地址范围。

  1. struct bio_vec {
  2. struct page *bv_page;
  3. unsigned int bv_len;
  4. unsigned int bv_offset;
  5. };

一次块设备传输请求,会涉及到很多个这样的不连续的物理空间。不连续的物理空间,不能直接使用DMA

这时,可以利用sg操作,让每个bio_vec结构,对应一个scatterlist结构:

  1. struct scatterlist {
  2. unsigned long page_link;
  3. unsigned int offset; /* buffer offset */
  4. dma_addr_t dma_address; /* dma address */
  5. unsigned int length; /* length */
  6. };

在MMC的请求处理函数中,遍历每一request中所有bio_vec结构,对应一个scatterlis结构描述:

  1. rq_for_each_segment(bvec, rq, iter) {
  2. ... ...
  3. sg = sg_next(sg); //指向sg链表中的下一个scatterlist
  4. sg_set_page(sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset); //使用sg描述一个页
  5. ... ...
  6. }
  7. ... ...
  8.   sg_mark_end(sg); //标志sg链表到此sg节点就结束了

上面sg这个链表初始化代码如下:

  1. sg_init_table(mq->bounce_sg, bouncesz / 512);
  2. 第一个参数为链表头,第二个为成员数量。

上边所涉及到的几个函数:

  1. void sg_init_table(struct scatterlist *sg, unsigned int nents);

sg是sg链表(数组)的表头,nents是要分配的数组的个数。

  1. void sg_set_page(struct scatterlist *sg, struct page *page,
  2. unsigned int len, unsigned int offset);
  3. void sg_set_buf(struct scatterlist *sg, const void *buf,
  4. unsigned int buflen);

使用指定的参数,填充sg结构。第一个以页为页地址,偏移量,长度为接线;第二个以地址和长度为界限。

  1. struct scatterlist *sg_next(struct scatterlist *sg);

返回下一个sg成员地址。

也有一个宏,来遍历sg链表上的所有sg结构,它的使用方法通常如下:

  1. int i;
  2. struct scatterlist *list, *sgentry;
  3.  
  4. /* Fill in list and pass it to dma_map_sg(). Then... */
  5. for_each_sg(i, list, sgentry, nentries) {
  6. program_hw(device, sg_dma_address(sgentry), sg_dma_len(sgentry));
  7. }

sgentry为sg链表入口,nentryies是sg数组(链表)总长。

【转】scatterlist && DMA的更多相关文章

  1. mmap DMA【转】

    转自:http://blog.csdn.net/lihaoweiv/article/details/6275241 第 13 章  mmap 和 DMA 本章将深入探讨 Linux 内存管理部分,并强 ...

  2. DMA过程分析

    1.1 当我们在应用程序中编写write系统调用,向磁盘中写入数据时,写入请求会先调用底层写函数,将请求先写入内存中的页快速缓存(page cache)中,写入成功则立马返回,真正的写入磁盘操作会延迟 ...

  3. dma 测试例子

    #include <linux/module.h> #include <linux/slab.h> #include <linux/sched.h> #includ ...

  4. Dynamic DMA mapping Guide

    一.前言 这是一篇指导驱动工程师如何使用DMA API的文档,为了方便理解,文档中给出了伪代码的例程.另外一篇文档dma-api.txt给出了相关API的简明描述,有兴趣也可以看看那一篇,这两份文档在 ...

  5. Linux内核scatterlist API介绍

    1. 前言 我们在那些需要和用户空间交互大量数据的子系统(例如MMC[1].Video.Audio等)中,经常看到scatterlist的影子.对我们这些“非英语母语”的人来说,初见这个词汇,脑袋瞬间 ...

  6. 《Linux Device Drivers》第十五章 内存映射和DMA——note

    简单介绍 很多类型的驱动程序编程都须要了解一些虚拟内存子系统怎样工作的知识 当遇到更为复杂.性能要求更为苛刻的子系统时,本章所讨论的内容迟早都要用到 本章的内容分成三个部分 讲述mmap系统调用的实现 ...

  7. linux内存管理之DMA

    说起DMA我们并不陌生,但是实际编程中去用的人不多吧,最多就是网卡驱动里的环形buffer,再有就是设备的dma,下面我们就分析分析.   DMA用来在设备内存和内存之间直接数据交互.而无需cpu干预 ...

  8. 二十二、DMA驱动

    一.DMA简介 DMA(Direct Memory Access,直接内存存取),DMA传输将数据从一个地址空间复制到另外一个地址空间.传输过程由DMA控制器独立完成,它并没有拖延CPU的工作,可以让 ...

  9. LDD3 第15章 内存映射和DMA

    本章内容分为三个部分: 第一部分讲述了mmap系统调用的实现过程.将设备内存直接映射到用户进程的地址空间,尽管不是所有设备都需要,但是能显著的提高设备性能. 如何跨越边界直接访问用户空间的内存页,一些 ...

随机推荐

  1. 201521123097《Java程序设计》第二周学习总结

    1.本周学习总结 (1)学习了java的一些类型和变量. (2)学习了码云的部分功能的使用. 2.书面作业 使用Eclipse关联jdk源代码,并查看String对象的源代码. 为什么要尽量频繁的对字 ...

  2. 201521123073 《Java程序设计》第11周学习总结

    1. 本周学习总结 2. 书面作业 本次PTA作业题集多线程 1.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1.1 除了使用synchronized修饰方法实现互斥同步访问, ...

  3. 201521123073 《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 1. finally 题目4-2 1.1 截图你的提交结果( ...

  4. virtualbox修改主机名

    virtualbox修改主机名 /etc/hostname /etc/hosts

  5. JS中关于数组的内容

      前  言 LIUWE 在网站制作过程中,数组可以说是起着举足轻重的地位.今天就给大家介绍一下数组的一些相关内容.例如:如何声明一个数组和在网站制作过程中我们常用的一些数组的方法.介绍的不好还请多多 ...

  6. [js高手之路] html5 canvas系列教程 - 文本样式(strokeText,fillText,measureText,textAlign,textBaseline)

    接着上文线条样式[js高手之路] html5 canvas系列教程 - 线条样式(lineWidth,lineCap,lineJoin,setLineDash)继续. canvas提供两种输出文本的方 ...

  7. Python dict operation introduce

    字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,格式如下所示: d = ...

  8. 在JavaScript中使用json.js:Ajax项目之GET请求(同步)

    1.用php编写一个提供数据的响应程序(phpdata.php) <?php $arr=array(1,2,3,4); //将数组编码为JSON格式的数据 $jsonarr=json_encod ...

  9. 关于 LindedList 我想说

    LinkedList 的一些认识: 继承于AbstractSequentialList的双向链表,可以被当作堆栈.队列或双端队列进行操作 有序,非线程安全的双向链表,默认使用尾部插入法 适用于频繁新增 ...

  10. Count Color 线段树

    Count Color Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit  ...