原文: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. 201521123014 《Java程序设计》第3周学习总结

    1. 本周学习总结 2. 书面作业 Q1. 代码阅读 public class Test1 { private int i = 1;//这行不能修改 private static int j = 2; ...

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

    1. 本周学习总结 1.Java对我来说难度还是很大.需要花更多时间去学习. 2. 书面作业 1.使用Eclipse关联jdk源代码,并查看String对象的源代码(截图)?分析String使用什么来 ...

  3. VBScript中InStr函数的用法

    InStr([start, ]str1, str2[, compare]) [用途]:返回str2在str1中的位置.匹配成功时,返回值最小值为1,未匹配到时返回0. [参数说明]: start:在s ...

  4. 201521123056 《Java程序设计》第11周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1. 互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) ...

  5. java第十三次作业

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相内关容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...

  6. 201521123050 《Java程序设计》第13周学习总结

    1. 本周学习总结 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu.edu.cn,分析返回结果有何不同?为什么会有这样的不同? 对比可以看出 ...

  7. shell脚本之流程控制

      shell脚本之流程控制 shell脚本之流程控制 条件语句 条件判断 循环语句for,while,until for循环 while循环 until循环 循环控制语句continue 循环控制语 ...

  8. jquery-easyUI第一篇【介绍、入门、使用常用的组件】

    什么是easyUI 我们可以看官方对easyUI的介绍: easyUI就是一个在Jquery的基础上封装了一些组件-.我们在编写页面的时候,就可以直接使用这些组件-非常方便-easyUI多用于在后台的 ...

  9. Web 项目更改项目名

    简单的记录web开发中基本的操作. 更改项目名 直接修改 找到原项目中的.project 文件,更改中项目名称.然后在同目录下找到.mymetadata 文件 并更改name.context-root ...

  10. POJ 3923 Ugly Windows(——考察思维缜密性的模拟题)

    题目链接: http://poj.org/problem?id=3923 题意描述: 输入一个n*m的屏幕 该屏幕内有至少一个对话框(每个对话框都有对应的字母表示) 判断并输出该屏幕内处于最表层的对话 ...