原文: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地址掩码:

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

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

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

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

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

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

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

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

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

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

对于单个已经分配的缓冲区而言,使用dma_map_single()可实现流式DMA映射:
dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);
如果映射成功,返回的是总线地址,否则返回NULL.最后一个参数DMA的方向,可能取DMA_TO_DEVICE, DMA_FORM_DEVICE, DMA_BIDIRECTIONAL和DMA_NONE;
与之对应的反函数是:
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 描述,它代表一段物理地址范围。

struct bio_vec {
struct page *bv_page;
unsigned int bv_len;
unsigned int bv_offset;
};

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

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

struct scatterlist {
unsigned long page_link;
unsigned int offset; /* buffer offset */
dma_addr_t dma_address; /* dma address */
unsigned int length; /* length */
};

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

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

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

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

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

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

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

void sg_set_page(struct scatterlist *sg, struct page *page,
unsigned int len, unsigned int offset);
void sg_set_buf(struct scatterlist *sg, const void *buf,
unsigned int buflen);

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

struct scatterlist *sg_next(struct scatterlist *sg);

返回下一个sg成员地址。

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

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

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. 201521044091 《Java程序设计》第2周学习总结

    1本章学习总结 (1)一些java的基本语法 (2)java API文件 (3)使用码云管理自己的代码 2.Java Q&A 1)使用Eclipse关联jdk源代码(截图),并查看String ...

  2. 201521123060 《Java程序设计》第9周学习总结

    1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2.书面作业 本次PTA作业题集异常 1.常用异常 题目5-1 1.1截图你的提交结果(出现学号) 1.2自己以前编写 ...

  3. JAVA课程设计 计算器模拟程序 王华俊(201521123015)

    1.团队课程设计博客链接 http://www.cnblogs.com/yuanj/p/7072137.html 2.个人负责模块或任务说明 GUI界面设计 各类之间拼接 3.自己的代码提交记录截图 ...

  4. linux下svn命令大全(转)

    1.将文件checkout到本地目录 svn checkout path(path是服务器上的目录) 例如:svn checkout svn://192.168.1.1/pro/domain 简写:s ...

  5. hadoop源码import到eclipse工程

    1.解压hadoop-1.1.2.tar.gz,重点在src文件夹 2.在eclipse中通过菜单栏创建一个java工程,工程名随便 3.在创建的工程上,点击右键,在弹出菜单中选择最后一项,在弹出窗口 ...

  6. PeopleRank

    PeopleRank:基于PageRank的理论,以每个微博账户的“关注”为链出链接,“粉丝”为链入链接的这种以人为核心的关系. PeopleRank假设条件:– 数量假设:如果一个用户节点接收到的其 ...

  7. 基于c编写的关于随机生成四则运算的小程序

    基于http://www.cnblogs.com/HAOZHE/p/5276763.html改编写的关于随机生成四则运算的小程序 github源码和工程文件地址:https://github.com/ ...

  8. jQuery自定义插件--banner图滚动

    前言 jQuery是一个功能强大的库,提供了开发JavaScript项目所需的所有核心函数.很多时候我们使用jQuery的原因就是因为其使用插件的功能,然而,有时候我们还是需要使用自定义代码来扩展这些 ...

  9. NopCommerce添加事务机制

    NopCommerce现在最新版是3.9,不过依然没有事务机制.作为一个商城,我觉得事务也还是很有必要的.以下事务代码以3.9版本作为参考: 首先,IDbContext接口继承IDisposable接 ...

  10. 为什么Java中的String类是不可变的?

    String类是Java中的一个不可变类(immutable class). 简单来说,不可变类就是实例在被创建之后不可修改. 在<Effective Java> Item 15 中提到了 ...