用户空间scatter gether方式DMA传输

本文探讨了如何在Linux环境下实现类似于Win2000 DDK中提供的物理地址映射功能,包括如何构建散列列表和利用PCI映射来实现数据的零拷贝传输。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标  题: [合集] 请问如何实现这个问题? 
发信站: 水木社区 (Sun Dec 24 14:00:17 2006), 站内 
  
☆—————————————————————————————————————☆
   
terrac (xiaoyt) 于 (Sat Mar  6 14:26:12 2004) 提到: 
  
在win2000 DDK 中有个接口函数: 
PVOID ScsiPortGetPhysicalAddress( IN PIDE_Channel IdeChannelIdeChannel, IN PSC 
SI_REQUEST_BLOCK Srb,IN PVOID   dataPointer, OUT ULONG *length) ; 
它的作用是将 用户空间的一个首地址为dataPointer (假设长度为count)的连续数据块 
,映射为一个内核空间不连续的离散数据块集合,该离散数据块的第一个数据块的块基地 
址为该函数的返回值(设为pysAddress1),第一个数据块的长度由length 返回。 
(这样,当第二次再次调用 pysAddress2=ScsiPortGetPhysicalAddress(IdeChannel, S 
rb, dataPointer+length,&length2)时,得到第二块内核空间数据块,块首地址和块长 
度分别为pysAddress2和length2,以次类推 。 
问题时,在linux下,如何实现这个映射?有这样的系统调用吗?如果没有,如何实现? 
  
  
  
linux下在 drivers/ide/ide_dma.c中有个建立scatterlist的函数如下: 
  
/** 
  *    ide_build_sglist    -    map IDE scatter gather for DMA I/O 
  *    @hwif: the interface to build the DMA table for 
  *    @rq: the request holding the sg list 
  *    @ddir: data direction 
  * 
  *    Perform the PCI mapping magic neccessary to access the source or 
  *    target buffers of a request via PCI DMA. The lower layers of the 
  *    kernel provide the neccessary cache management so that we can 
  *    operate in a portable fashion 
  */ 
   
static int ide_build_sglist (ide_hwif_t *hwif, struct request *rq, int ddir) 

     struct buffer_head *bh; 
     struct scatterlist *sg = hwif->sg_table; 
     unsigned long lastdataend = ~0UL; 
     int nents = 0; 
  
     if (hwif->sg_dma_active) 
         BUG(); 
  
     bh = rq->bh; 
     do { 
         int contig = 0; 
  
         if (bh->b_page) { 
             if (bh_phys(bh) == lastdataend) 
                 contig = 1; 
         } else { 
             if ((unsigned long) bh->b_data == lastdataend) 
                 contig = 1; 
         } 
  
         if (contig) { 
             sg[nents - 1].length += bh->b_size; 
             lastdataend += bh->b_size; 
             continue; 
         } 
  
         if (nents >= PRD_ENTRIES) 
             return 0; 
  
         memset(&sg[nents], 0, sizeof(*sg)); 
  
         if (bh->b_page) { 
             sg[nents].page = bh->b_page; 
             sg[nents].offset = bh_offset(bh); 
             lastdataend = bh_phys(bh) + bh->b_size; 
         } else { 
             if ((unsigned long) bh->b_data < PAGE_SIZE) 
                 BUG(); 
  
             sg[nents].address = bh->b_data; 
             lastdataend = (unsigned long) bh->b_data + bh->b_size; 
         } 
  
         sg[nents].length = bh->b_size; 
         nents++; 
     } while ((bh = bh->b_reqnext) != NULL); 
  
     if(nents == 0) 
         BUG(); 
          
     hwif->sg_dma_direction = ddir; 
     return pci_map_sg(hwif->pci_dev, sg, nents, ddir); 
}              
      
    该函数也基本上是在构建散离表,但是我的那个设备根本不设计到扇区、块等块设备参 
数和变量。只是需要将用户空间的地址映射为物理地址。因此也无法构造struct request 
  和struct buffer_head 结构。但想避开这些结构,如何操作? 
     恳请大侠救命! 
  
  
  
☆—————————————————————————————————————☆
   
terrac (xiaoyt) 于 (Mon Mar  8 12:29:08 2004) 提到: 
  
这几天很痛苦的查了大量资料,只有一点想法,但还是不敢确定,还恳请高手指点! 
  
(在driver中) 
先用remap_page_range建立从用户空间地址到物理地址的映射,得到一个物理地址 
  void * buffer.然后进行强制转换: 
struct scatterlist * sgList = (struct scatterlist *)buffer; 
然后再将sgList的每一项copy到一个要建的散离表各项中。 
我没有读过linux内核,这些只是猜测。不知各位高手有何看法? 
  
附scatterlist结构如下: 
struct scatterlist { 
     char *  address;    /* Location data is to be transferred to, NULL for 
              * highmem page */ 
     struct page * page; /* Location for highmem page, if any */ 
     unsigned int offset;/* for highmem, page offset */ 
  
     dma_addr_t dma_address; 
     unsigned int length; 
}; 
  
  
  
  
  
【 在 terrac (xiaoyt) 的大作中提到: 】 
: 在win2000 DDK 中有个接口函数: 
: PVOID ScsiPortGetPhysicalAddress( IN PIDE_Channel IdeChannelIdeChannel, IN.. 
: SI_REQUEST_BLOCK Srb,IN PVOID   dataPointer, OUT ULONG *length) ; 
: 它的作用是将 用户空间的一个首地址为dataPointer (假设长度为count)的连续数.. 
: ,映射为一个内核空间不连续的离散数据块集合,该离散数据块的第一个数据块的块.. 
: 址为该函数的返回值(设为pysAddress1),第一个数据块的长度由length 返回。 
: (这样,当第二次再次调用 pysAddress2=ScsiPortGetPhysicalAddress(IdeChanne.. 
: rb, dataPointer+length,&length2)时,得到第二块内核空间数据块,块首地址和.. 
: 度分别为pysAddress2和length2,以次类推 。 
: 问题时,在linux下,如何实现这个映射?有这样的系统调用吗?如果没有,如何实�.. 
: ................... 
  
  
  
☆—————————————————————————————————————☆
   
terrac (xiaoyt) 于 (Tue Mar  9 11:32:04 2004) 提到: 
  
我目前在写一个driver。driver做的工作就是:  
用户程序写时(即发送数据),实现xxx_write(struct file *filp,char *buff,size_t  
cont,loff_t * offset) 时,不想先把用户数据拷贝到kernel空间,而是直接将用户空间 
连续的数据映射成内核离散的数据,并锁定这些用户数据,这样将得到一个散离表(scat 
terlist)。driver 将这个散离表基地址写进设备的SCAT_GATHER_TX_BASE_ADDRESS 寄存 
器,并启动DMA传输,即可(然后等到中断服务程序唤醒)。读时(即接受数据时,同样, 
只需将内核接受数局的散离表基地址写入 SCAT_GATHER_RX_BASE_ADDRESS 寄存器中,然后 
再启动DMA传输。  
      linux下提供了一种用scatter/gather的DMA方式,pci_map_sg(), 实现虚地址映射。 
scatter/gather就是为支持它的硬件实现 zero copy的一种途径。scatter/gather 可以一 
次DMA传多个buffer块. 这样就在setup dma的时候指定一系列的buffer( offset, len).  
(一般来说DMA到一个连续的内存上.也有能够DMA到不连续上面的.)也就是直接让硬件DM 
A传到用户空间的buffer里面. 
      我现在遇到的问题时,我不知道如何实现直接从I/O内存读/写数据到用户空间,如何将用户空间映射成DMA离散的内核空间,不知道如何构建散离表。(即不知道怎样才能得到散离表基地址)。    
  
  
在win2000 DDK 中有个接口函数: 
PVOID ScsiPortGetPhysicalAddress( IN PIDE_Channel IdeChannelIdeChannel, IN PSC 
SI_REQUEST_BLOCK Srb,IN PVOID   dataPointer, OUT ULONG *length) ; 
它的作用是将 用户空间的一个首地址为dataPointer (假设长度为count)的连续数据块 
,映射为一个内核空间不连续的离散数据块集合,该离散数据块的第一个数据块的块基地 
址为该函数的返回值(设为pysAddress1),第一个数据块的长度由length 返回。 
(这样,当第二次再次调用 pysAddress2=ScsiPortGetPhysicalAddress(IdeChannel, S 
rb, dataPointer+length,&length2)时,得到第二块内核空间数据块,块首地址和块长 
度分别为pysAddress2和length2,以次类推 。 
问题时,在linux下,如何实现这个映射?有这样的系统调用吗?如果没有,如何实现? 
  
我的那个设备根本不设计到扇区、块等块设备参 
数和变量。只是需要将用户空间的地址映射为物理地址。因此也无法构造struct request 
  和struct buffer_head 结构。但想避开这些结构,如何操作? 
  
  
我觉得这个问题对于高手应当很普通的,可是对于我就很困难了。我又重新看了mmap, 
不知用kiobuf能否实现?我对mmap这章总是难以理解,对kiobuf也是。希望理解kiobuf很深刻的高手帮忙解答一下。 
  
  
【 在 terrac (xiaoyt) 的大作中提到: 】 
: 这几天很痛苦的查了大量资料,只有一点想法,但还是不敢确定,还恳请高手指点! 
: (在driver中) 
: 先用remap_page_range建立从用户空间地址到物理地址的映射,得到一个物理地址 
:  void * buffer.然后进行强制转换: 
: struct scatterlist * sgList = (struct scatterlist *)buffer; 
: 然后再将sgList的每一项copy到一个要建的散离表各项中。 
: 我没有读过linux内核,这些只是猜测。不知各位高手有何看法? 
: 附scatterlist结构如下: 
: struct scatterlist { 
:     char *  address;    /* Location data is to be transferred to, NULL for 
:              * highmem page */ 
: ................... 
  
  
  
☆—————————————————————————————————————☆
   
voidkey (探索者) 于 (Thu Mar 11 17:35:12 2004) 提到: 
  
要实现外设直接dma到用户空间既zero copy,我的想法是: 
1.先保证用户空间缓冲区全部进行了映射,这可以参考map_user_kiobuf的实现,该函数 
   在mm/memory.c中,如果直接使用这个函数,就会得到一个struct kiobuf结构变量 
2.进行dma映射 
   这可以使用多次pci_map_single 
   也可以构造scatterlist然后使用pci_map_sg 
3.把一系列dma地址写到外设相应的寄存器中 
  
【 在 terrac (xiaoyt) 的大作中提到: 】 
: 我目前在写一个driver。driver做的工作就是:   
: 用户程序写时(即发送数据),实现xxx_write(struct file *filp,char *buff,size_t   
: cont,loff_t * offset) 时,不想先把用户数据拷贝到kernel空间,而是直接将用户空间 
: 连续的数据映射成内核离散的数据,并锁定这些用户数据,这样将得到一个散离表(scat 
: terlist)。driver 将这个散离表基地址写进设备的SCAT_GATHER_TX_BASE_ADDRESS 寄存 
: 器,并启动DMA传输,即可(然后等到中断服务程序唤醒)。读时(即接受数据时,同样, 
: 只需将内核接受数局的散离表基地址写入 SCAT_GATHER_RX_BASE_ADDRESS 寄存器中,然后 
: 再启动DMA传输。   
:      linux下提供了一种用scatter/gather的DMA方式,pci_map_sg(), 实现虚地址映射。 
: scatter/gather就是为支持它的硬件实现 zero copy的一种途径。scatter/gather 可以一 
: 次DMA传多个buffer块. 这样就在setup dma的时候指定一系列的buffer( offset, len).   
: (一般来说DMA到一个连续的内存上.也有能够DMA到不连续上面的.)也就是直接让硬件DM 
: A传到用户空间的buffer里面. 
:      我现在遇到的问题时,我不知道如何实现直接从I/O内存读/写数据到用户空间,如何将用户空间映射成DMA离散的内核空间,不知道如何构建散离表。(即不知道怎样才能得到散离表基地址)。     
: 在win2000 DDK 中有个接口函数: 
: PVOID ScsiPortGetPhysicalAddress( IN PIDE_Channel IdeChannelIdeChannel, IN PSC 
: SI_REQUEST_BLOCK Srb,IN PVOID   dataPointer, OUT ULONG *length) ; 
: 它的作用是将 用户空间的一个首地址为dataPointer (假设长度为count)的连续数据块 
: ,映射为一个内核空间不连续的离散数据块集合,该离散数据块的第一个数据块的块基地 
: 址为该函数的返回值(设为pysAddress1),第一个数据块的长度由length 返回。 
: (这样,当第二次再次调用 pysAddress2=ScsiPortGetPhysicalAddress(IdeChannel, S 
: rb, dataPointer+length,&length2)时,得到第二块内核空间数据块,块首地址和块长 
: 度分别为pysAddress2和length2,以次类推 。 
: 问题时,在linux下,如何实现这个映射?有这样的系统调用吗?如果没有,如何实现? 
: 我的那个设备根本不设计到扇区、块等块设备参 
: 数和变量。只是需要将用户空间的地址映射为物理地址。因此也无法构造struct request 
:  和struct buffer_head 结构。但想避开这些结构,如何操作? 
: 我觉得这个问题对于高手应当很普通的,可是对于我就很困难了。我又重新看了mmap, 
: 不知用kiobuf能否实现?我对mmap这章总是难以理解,对kiobuf也是。希望理解kiobuf很深刻的高手帮忙解答一下。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值