关闭

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

标签: 测试linuxdma
2228人阅读 评论(0) 收藏 举报
分类:

本来拟定是想直接在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通道分配成功,或者失败,没有自动分配一说。问什么会失败,后面会讲
  1. 如何确定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;
    };

  2. 为什么会分配冲突?
    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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:285147次
    • 积分:3860
    • 等级:
    • 排名:第8247名
    • 原创:89篇
    • 转载:117篇
    • 译文:6篇
    • 评论:18条
    最新评论