gpt4 book ai didi

Linux 内核设备驱动程序从设备到用户空间内存的 DMA

转载 作者:IT王子 更新时间:2023-10-29 00:13:21 29 4
gpt4 key购买 nike

我想尽快从支持 DMA 的 PCIe 硬件设备获取数据到用户空间。

问:如何将“到用户空间的直接 I/O 与/和/通过 DMA 传输相结合”

  1. 通读LDD3,似乎我需要执行几种不同类型的IO操作!?

    dma_alloc_coherent 为我提供了可以传递给硬件设备的物理地址。但是需要设置 get_user_pages 并在传输完成时执行 copy_to_user 类型的调用。这似乎是一种浪费,要求设备 DMA 进入内核内存(充当缓冲区),然后再次将其传输到用户空间。LDD3 p453: /* 只有现在访问缓冲区、复制给用户等才是安全的 */

  2. 理想情况下,我想要的是一些内存:

    • 我可以在用户空间中使用(也许通过 ioctl 调用请求驱动程序来创建 DMA 可用内存/缓冲区?)
    • 我可以从中获取一个物理地址并传递给设备,这样所有用户空间所要做的就是对驱动程序执行读取
    • read 方法将激活 DMA 传输,阻塞等待 DMA 完成中断并随后释放用户空间读取(用户空间现在可以安全使用/读取内存)。

我是否需要使用 get_user_pages dma_map_page 映射的单页流映射、设置映射和用户空间缓冲区?

到目前为止,我的代码在用户空间的给定地址处设置了 get_user_pages(我称之为直接 I/O 部分)。然后,dma_map_page 使用来自 get_user_pages 的页面。我将 dma_map_page 的返回值作为 DMA 物理传输地址提供给设备。

我正在使用一些内核模块作为引用:drivers_scsi_st.cdrivers-net-sh_eth.c。我会查看 infiniband 代码,但找不到最基本的代码!

非常感谢。

最佳答案

实际上我现在正在做完全相同的事情,我正在走 ioctl() 路线。总体思路是让用户空间分配用于 DMA 传输的缓冲区,并使用 ioctl() 将此缓冲区的大小和地址传递给设备驱动程序。然后,驱动程序将使用分散-收集列表以及流式 DMA API 将数据直接传输到设备和用户空间缓冲区或从中传输数据。

我正在使用的实现策略是驱动程序中的 ioctl() 进入一个循环,DMA 以 256k 的 block 为用户空间缓冲区(这是硬件强加的限制,用于多少分散/收集它可以处理的条目)。这被隔离在一个函数中,该函数会阻塞直到每次传输完成(见下文)。当所有字节都被传输或增量传输函数返回错误时,ioctl() 退出并返回到用户空间

ioctl() 的伪代码

/*serialize all DMA transfers to/from the device*/
if (mutex_lock_interruptible( &device_ptr->mtx ) )
return -EINTR;

chunk_data = (unsigned long) user_space_addr;
while( *transferred < total_bytes && !ret ) {
chunk_bytes = total_bytes - *transferred;
if (chunk_bytes > HW_DMA_MAX)
chunk_bytes = HW_DMA_MAX; /* 256kb limit imposed by my device */
ret = transfer_chunk(device_ptr, chunk_data, chunk_bytes, transferred);
chunk_data += chunk_bytes;
chunk_offset += chunk_bytes;
}

mutex_unlock(&device_ptr->mtx);

增量传递函数的伪代码:

/*Assuming the userspace pointer is passed as an unsigned long, */
/*calculate the first,last, and number of pages being transferred via*/

first_page = (udata & PAGE_MASK) >> PAGE_SHIFT;
last_page = ((udata+nbytes-1) & PAGE_MASK) >> PAGE_SHIFT;
first_page_offset = udata & PAGE_MASK;
npages = last_page - first_page + 1;

/* Ensure that all userspace pages are locked in memory for the */
/* duration of the DMA transfer */

down_read(&current->mm->mmap_sem);
ret = get_user_pages(current,
current->mm,
udata,
npages,
is_writing_to_userspace,
0,
&pages_array,
NULL);
up_read(&current->mm->mmap_sem);

/* Map a scatter-gather list to point at the userspace pages */

/*first*/
sg_set_page(&sglist[0], pages_array[0], PAGE_SIZE - fp_offset, fp_offset);

/*middle*/
for(i=1; i < npages-1; i++)
sg_set_page(&sglist[i], pages_array[i], PAGE_SIZE, 0);

/*last*/
if (npages > 1) {
sg_set_page(&sglist[npages-1], pages_array[npages-1],
nbytes - (PAGE_SIZE - fp_offset) - ((npages-2)*PAGE_SIZE), 0);
}

/* Do the hardware specific thing to give it the scatter-gather list
and tell it to start the DMA transfer */

/* Wait for the DMA transfer to complete */
ret = wait_event_interruptible_timeout( &device_ptr->dma_wait,
&device_ptr->flag_dma_done, HZ*2 );

if (ret == 0)
/* DMA operation timed out */
else if (ret == -ERESTARTSYS )
/* DMA operation interrupted by signal */
else {
/* DMA success */
*transferred += nbytes;
return 0;
}

中断处理程序非常简短:

/* Do hardware specific thing to make the device happy */

/* Wake the thread waiting for this DMA operation to complete */
device_ptr->flag_dma_done = 1;
wake_up_interruptible(device_ptr->dma_wait);

请注意,这只是一种通用方法,过去几周我一直在研究这个驱动程序,但还没有实际测试它......所以请不要把这个伪代码当作福音,而是务必仔细检查所有逻辑和参数 ;-)。

关于Linux 内核设备驱动程序从设备到用户空间内存的 DMA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5539375/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com