您的位置:首页 > 其它

dma传输与memcpy传输对比测试结果分析。。。

2015-11-20 11:42 381 查看
本来拟定是想直接在x86的服务器上测试,但是发现执行时一直获取不到dma通道,只能在arm架构下尝试。

1.测试代码,如下,网上找的,驱动的环境搭建之前

/*
* DMA test module
*
* Copyright (C) 2007 KEDACOM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/highmem.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/async_tx.h>
#include <linux/jiffies.h>

#define XFER_TIMES 4000
#define XFER_LEN  1<<20

void *dma_src,*dma_dest;
struct dma_chan *chan = NULL;

static int __init dmatest_init(void)
{

int xfer_order = get_order(XFER_LEN);
int i,ret ;
dma_cap_mask_t mask;
dma_cookie_t cookie;
enum dma_status status;
u64 j1,j2;

dma_src = __get_free_pages(GFP_KERNEL | GFP_DMA, xfer_order);
if (!dma_src) {
printk(KERN_ALERT "dma_src :alloc memory fail.n");
ret = -ENOMEM;
goto CLEAN;

}

dma_dest = __get_free_pages(GFP_KERNEL | GFP_DMA, xfer_order);
if (!dma_dest) {
printk(KERN_ALERT "dma_dest :alloc memory fail.n");
ret = -ENOMEM;
goto CLEAN;
}
printk(KERN_NOTICE"dma_src=%#x,dma_dest=%#xn",dma_src,dma_dest);
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
chan = dma_request_channel(mask, NULL, NULL);

if (chan) {
printk(KERN_NOTICE "dma_request_channel ok,current channel is : %sn",dma_chan_name(chan));
}else {
printk(KERN_NOTICE "dma_request_channel fail,no dma channel available.n");
ret = -1;
goto CLEAN;
}

j1 = get_jiffies_64();
for(i =0;i<XFER_TIMES;i++) {
cookie = dma_async_memcpy_buf_to_buf(chan, dma_dest, dma_src, XFER_LEN);
if (dma_submit_error(cookie)) {
printk(KERN_NOTICE"submit errorn");
ret = -1;
goto CLEAN;
}
}

dma_async_memcpy_issue_pending(chan);
do {
status = dma_async_memcpy_complete(chan, cookie, NULL, NULL);

} while (status == DMA_IN_PROGRESS);

if (status != DMA_SUCCESS)
printk(KERN_NOTICE "dma xfer dont accomplish,status=%dn",status);
j2 = get_jiffies_64();
printk(KERN_NOTICE"dma xfer time cost:%d ms.n",jiffies_to_msecs(j2-j1));

j1 = get_jiffies_64();
for(i =0;i<XFER_TIMES;i++){
memcpy(dma_dest, dma_src, XFER_LEN);
}
j2 = get_jiffies_64();
printk(KERN_NOTICE"memcpy time cost:%d ms.n",jiffies_to_msecs(j2-j1));
return 0;

CLEAN:
if (chan)
dma_release_channel(chan);

if (dma_src)
free_pages(dma_src,xfer_order);

if (dma_dest)
free_pages(dma_dest,xfer_order);

return ret;

}
/* when compiled-in wait for drivers to load first */
module_init(dmatest_init);

static void __exit dmatest_exit(void)
{
if (chan)
dma_release_channel(chan);

if (dma_src)
free_pages(dma_src,get_order(XFER_LEN));

if (dma_dest)
free_pages(dma_dest,get_order(XFER_LEN));

}
module_exit(dmatest_exit);

MODULE_AUTHOR("ZhangZhuan <zhangzhuan@kedacom.com>");
MODULE_LICENSE("GPL v2");


[1]

从网上down了一份驱动测试代码

2.测试过程

初始执行结果:dma_request_channel fail, no dma channel available,n

(1)打开dma通道

使用hdparm命令,

执行:hdparm -d1 /dev/sda

其中d 1表示使能DMA,你可以将其加到rc.locl中以便每次启动时都硬盘都能使用DMA方式传输数据。

结果:HDIO_GET_DMA failed: Inappropriate ioctl for device

调试:

网上有类似的返回打印,

“通过dmesg | grep [^d]ata

[ 28.210551] libata version 3.00 loaded.

[ 28.476513] pata_amd 0000:00:06.0: version 0.3.10

[ 28.483189] scsi0 : pata_amd

[ 28.485133] scsi1 : pata_amd

[ 28.485809] ata1: PATA max UDMA/133 cmd 0x1f0 ctl 0x3f6 bmdma 0xf000 irq 14

[ 28.485813] ata2: PATA max UDMA/133 cmd 0x170 ctl 0x376 bmdma 0xf008 irq 15

[ 28.997204] ata1.00: ATA-6: ST340014A, 8.01, max UDMA/100

[ 28.997209] ata1.00: 78165360 sectors, multi 16: LBA48

[ 28.997229] ata1.01: ATAPI: TSSTcorpDVD-ROM TS-H352C, CH01, max UDMA/33

[ 29.013102] ata1.00: configured for UDMA/100

[ 29.200806] ata1.01: configured for UDMA/33

[ 29.200847] ata2: port disabled. ignoring.

[ 29.202076] sata_nv 0000:00:08.0: version 3.5

[ 29.203546] scsi2 : sata_nv

[ 29.204104] scsi3 : sata_nv

[ 29.204314] ata3: SATA max UDMA/133 cmd 0x9f0 ctl 0xbf0 bmdma 0xdc00 irq 19

[ 29.204318] ata4: SATA max UDMA/133 cmd 0x970 ctl 0xb70 bmdma 0xdc08 irq 19

[ 29.512437] ata3: SATA link down (SStatus 0 SControl 300)

[ 29.832255] ata4: SATA link down (SStatus 0 SControl 300)

pata是并口硬盘(即IDE硬盘),sata是串口硬盘,DMA只属于pata的概念,而不属于sata, 因此,如果系统中的硬盘是由sata驱动的pata硬盘(即IDE硬盘),那么用hdparm检测得出的 HDIO_GET_DMA failed: Inappropriate ioctl for devic 的信息完全是正常的。 我的硬盘是IDE的,但我网络安装的Ubuntu里面强行使用sata驱动,我也只能用它,如果想改变的话重新编译内核应该可以解决。虽然硬盘旧,但我的主板有sata接口,以后买新硬盘记住要买串口的”【2】

经过查看,本机上并不是IDE硬盘,又有另一种对于SATA硬盘:

-------------------------------------------------------------------------------------------------------------------------

[root@anima lwg]# /sbin/hdparm -t /dev/sda

/dev/sda:
Timing buffered disk reads: 100 MB in 3.02 seconds = 33.11 MB/sec
[root@anima lwg]# /sbin/hdparm -c 1 -d 1 /dev/sda

/dev/sda:
setting 32-bit IO_support flag to 1
HDIO_SET_32BIT failed: Invalid argument
setting using_dma to 1 (on)
HDIO_SET_DMA failed: Inappropriate ioctl for device
IO_support   = 0 (default 16-bit)
[root@anima lwg]#sudo hdparm -i /dev/sda 查看UDMA使用模式,带*号的为正在使用的模式

查看控制器:
---------------------------

[root@anima lwg]# /sbin/lspci -v | grep IDE
00:0f.0 IDE interface: VIA Technologies, Inc. VIA VT6420 SATA RAID Controller (rev 80) (prog-if 8f [Master SecP SecO PriP PriO])
00:0f.1 IDE interface: VIA Technologies, Inc. VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06) (prog-if 8a [Master SecP PriP])
[root@anima lwg]#
打开内核的SATA选项,则不能使用SATA的DMA模式,否则使用/sbin/hdparm命令时会出错。
查看内核配置:


[root@anima lwg]# cat /boot/config-2.6.9-42.0.10.EL | grep ATA
# CONFIG_X86_MCE_NONFATAL is not set
# ATA/ATAPI/MFM/RLL support
# CONFIG_BLK_DEV_IDE_SATA is not set
CONFIG_SCSI_SATA=y
CONFIG_SCSI_SATA_AHCI=m
CONFIG_SCSI_SATA_SVW=m
CONFIG_SCSI_ATA_PIIX=m
CONFIG_SCSI_SATA_MV=m
CONFIG_SCSI_SATA_NV=m
CONFIG_SCSI_SATA_QSTOR=m
CONFIG_SCSI_SATA_PROMISE=m
CONFIG_SCSI_SATA_SX4=m
CONFIG_SCSI_SATA_SIL=m
CONFIG_SCSI_SATA_SIL24=m
CONFIG_SCSI_SATA_SIS=m
CONFIG_SCSI_SATA_ULI=m
CONFIG_SCSI_SATA_VIA=m
CONFIG_SCSI_SATA_VITESSE=m
CONFIG_SCSI_SATA_INTEL_COMBINED=y
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_EATA_PIO is not set
# CONFIG_ATALK is not set
CONFIG_USB_STORAGE_DATAFAB=y
# CONFIG_INFINIBAND_IPOIB_DEBUG_DATA is not set
# CONFIG_ATARI_PARTITION is not set
[root@anima lwg]#


需要打开内核的如下两个选项:

CONFIG_SCSI_SATA=y

CONFIG_SCSI_ATA_PIIX=y

重新编译内核。

如果你的SATA为启动盘的话,可能需要把原来/boot/grub/grub.conf 和 /etc/fstab 中的hda更改为sda,其他的驱动器名称也可能需要进行更改。[3]

按照上述操作后的结果:

CONFIG_SCSI_SAS_ATA=y
#   CONFIG_SCSI_EATA is not set


3.测试结论

dma一般用于内存和外设间的传输。arm下支持内存到内存的传输。x86下内存到内存不支持?

请教专家后得到的结论,前面看到的硬盘上的这种DMA都是在SATA总线控制器下,其DMA请求都是SATA Device向外主动发出的。而x86下目前没有看到单独的挂载在总线上的DMA芯片,所以也没有可以直接用来进行内存DDR到内存DDR的拷贝。但是arm架构下,所有的CPU,DDR芯片,SATA设备都是挂载L2总线上的,因此能够通过DMA芯片来完成多片或者内部DDR的DMA请求。

背景知识:

Q:

A:hdparm

-T perform cache read timings显示了不存取磁盘直接从Linux缓存读取数据的速度. 这项测量实际上标示了被测系统的处理器,缓存和内存的吞吐量.

-t perform device read timings显示了不使用预先的数据缓冲, 标示了Linux下没有任何文件系统开销时磁盘可以支持多快的连续数据读取.

关于硬盘是否打开DMA等情况,请不要在使用hdparm -d查询,原因是系统将你的硬盘被认作了/dev/sda,那么就是内核使用libata来支持你的硬盘(实际是IDE接口)。btw:现在的libata已经做得非常好了。

如果你对于libata有些认识的话,上面提到的红字部分就不是问题了。

引用:

HDIO_SET_32BIT failed: Invalid argument

libata不支持32bit操作,将来也许会支持,可能性不大.实际上也没有必要,具体可以查看相关知识以及 http://linux-ata.org

引用:

HDIO_SET_DMA failed: Inappropriate ioctl for device

libata默认使用DMA来传输数据,对于一些新型的硬盘,特别是SATA控制器的硬盘,DMA是基本的要求。

所以libata是不会同意你去修改DMA设置的,尽管你想打开,但是如果你想关闭呢,想想看如果系统使用SATA控制器在没有DMA的情况下如何传输数据,性能将会是什么性能,甚至硬盘都无法工作。相信你也不会同意随意修改DMA,再一次尽管你是想打开他。

那么如何查看硬盘的工作状态,可以使用

代码:

sudo hdparm -i /dev/sda

查看硬盘摘要信息,注意里面关于DMA部分,比如我的

代码:

UDMA modes: udma0 udma1 udma2 udma3 udma4 *udma5

其中udma5前面的星号表示该硬盘工作于udma5模式。

或者使用

代码:

sudo hdparm -I /dev/sda

查看硬盘详细信息。

从某种程度上来说,libata支持的硬盘控制器基本告别hdparm了。

Q:request_dma和dma_reque_channel这套好像不太一样,request_dma参数中的dmanr是否会自动分配?

A:

1. 系统会不会自动分配?

不会,只有ISA接口卡才会使用request_dma来向系统申请dma通道,PCI以后的设备都不再使用系统dma通道。源码为证:

int request_dma(unsigned int dmanr, const char * device_id)

{

if (dmanr >= MAX_DMA_CHANNELS)

return -EINVAL;

if (xchg(&dma_chan_busy[dmanr].lock, 1) != 0)
return -EBUSY;

dma_chan_busy[dmanr].device_id = device_id;

/* old flag was 0, now contains 1 to indicate busy */
return 0;
} /* request_dma */

可以看出来,或者你指定的dma通道分配成功,或者失败,没有自动分配一说。问什么会失败,后面会讲


如何确定dma通道号这个参数?

在ISA系统中,每个接口卡有一对DREQ/DACK,你查看硬件电路可以知道这对线和8237A的哪一对DREQ/DACK连在一起的,如果连接到8237A的通道0,这个参数就是0,如果是通道7,那这个参数就是7.软驱一般都是接到通道0,所以看软驱代码,申请的也是0号通道:

drivers/acorn/block/fd1772.c

if (request_dma(FLOPPY_DMA(0), “fd1772”)) {

printk(“Unable to grab DMA%d for the floppy (1772) driver\n”, FLOPPY_DMA);

goto err_blkdev;

};

为什么会分配冲突?

ISA总线上可能会有多个slot,由于只有7个通道可用,如果两块卡不小心用了同一个dma通道,那么就会有一个设备的dma通道申请失败。

引文参考:

[1]http://www.ithao123.cn/content-8026108.html

[2]http://blog.chinaunix.net/uid-20587169-id-1919228.html

[3]http://blog.sina.com.cn/s/blog_59b0e3f30100ly7i.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: