我正在开发beaglebone black like board的驱动程序。司机与fpga交流。当fpga有数据要读取时,它会分解irq。所以我必须快速启动DMA事务来读取数据。是否可以从原子上下文启动事务?
现在我将任务发送到高优先级的工作队列并开始DMA事务处理但有时候,当我以30mb / s的速度工作时,我从irq处理程序调用queue_work
和从workqueue启动作业之间得到了大量的东西(大约200-500微秒)。
那么我可以直接从irq启动dma,还是有更快的方式从irq处理程序启动dma事务?
我用linux kernel 4.9。
更新:
void init(){
g_fpga_dma_queue = alloc_workqueue("fpga_dma_queue", WQ_UNBOUND |Q_HIGHPRI, 1);
}
static irq_handler_t irqReadyRead(unsigned int irq, void* dev_id, struct pt_regs* regs)
{
if(g_fpga_dma_queue) {
queue_work(g_fpga_dma_queue, &fpga_dma_work);
}
return (irq_handler_t)IRQ_HANDLED;
}
static void dma_callback_read(void *param)
{
struct dma_chan *chan = g_dma_chan_read;
switch (dma_async_is_tx_complete(chan, cookie_read, NULL, NULL)) {
case DMA_COMPLETE:
irqraised1_read = 1;
g_good_dma++;
break;
case DMA_ERROR:
irqraised1_read = -1;
g_bad_dma++;
break;
default:
irqraised1_read = -1;
g_bad_dma++;
break;
}
complete(&dma_comp_read);
}
static int read_dma(int count)
{
struct dma_device *dev;
struct dma_async_tx_descriptor *tx;
unsigned long flags;
int result = 0;
dev = g_dma_chan_read->device;
flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
tx = dev->device_prep_dma_memcpy(g_dma_chan_read, dmaphysbuf_read, (unsigned long) FPGA_READ_BUFFER_ADDR,
(size_t) count, flags);
if (!tx) {
DBG_LOG("device_prep_dma_memcpy failed\n");
return -ENODEV;
}
irqraised1_read = 0u;
dma_comp_read.done = 0;
/* set the callback and submit the transaction */
tx->callback = dma_callback_read;
tx->callback_param = NULL;
cookie_read = dmaengine_submit(tx);
dma_async_issue_pending(g_dma_chan_read);
wait_for_completion(&dma_comp_read);
/* Check the status of the completed transfer */
if (irqraised1_read < 0) {
DBG_LOG("edma copy: Event Miss Occured!!!\n");
dmaengine_terminate_all(g_dma_chan_read);
result = -EAGAIN;
}
}
static void fpga_dma_work_handler(struct work_struct *w){
size_t count = readSize();
read_dma();
}
有时候(当我们插入或删除usb flash时,当任何驱动程序在终端中写入消息时)我在irq处理程序和dma实际启动时(~1 ms)之间的延迟非常大
我找到了延误的主要原因。当任何驱动程序在控制台中打印出来时(当我插入usb,移除网络电缆,从驱动程序调用printk等)时,我的驱动程序出现了很大的延迟,通常我在工作队列中推送任务和启动大约100个dma事务之间有最大延迟-200微秒(准备dma事务约10-20微秒),现在还可以。
当我直接从irq处理程序启动它们时,我发现了一些dma事务以错误结束的原因,我每隔~500微秒就会中断,如果在irq处理程序之后延迟,并且在dma事务处理实际启动之前,我会在前一个事务结束之前启动新事务( dma事务应该在大约10微秒内完成,我不检查以前的事务是否完成,并且使用单线程工作队列,在上一个任务完成之前启动下一个任务是不可能的,所以当我将代码更改为时,我忘了检查事务是否已完成从irq处理程序启动事务)
现在我继续使用具有禁用内核输出的高优先级队列来控制台。如果我需要较小的延迟来启动dma事务,我可能会尝试使用tasklet insted workqueue(我认为从irq开始dma并不好,因为我的主板上的irq处理程序需要大约10-20微秒,但有时可能没问题)