从linux内核中的irq处理程序启动DMA事务

问题描述 投票:1回答:1

我正在开发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)之间的延迟非常大

linux-kernel linux-device-driver embedded-linux dma
1个回答
1
投票

我找到了延误的主要原因。当任何驱动程序在控制台中打印出来时(当我插入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微秒,但有时可能没问题)

© www.soinside.com 2019 - 2024. All rights reserved.