Каковы возможные причины того, что обратный вызов завершения DMA не вызывается? - PullRequest
0 голосов
/ 28 мая 2020

У меня есть плата Xilinx Zynq Ultrascale + MPSo C, FPGA содержит блок AXI DMA , который отправляет данные для получения приложением на Linux, работающем на процессор (ы) приложений Cortex A53. Используя промежуточный драйвер, dma-proxy , предоставленный в качестве примера Xilinx, который устраняет разрыв между драйвером Xilinx AXI DMA in-tree Linux и пользовательским пространством, я передаю данные из FPGA в приложение пользовательского пространства. Но в конце я получаю тайм-аут, так как завершение никогда не вызывается.

Ниже приведен результат dmesg того, что происходит, когда я вызываю тестовое приложение с параметрами для 1 передачи 128 байт и запустите FPGA для непрерывной отправки данных. Как видно из источника, тайм-аут установлен на 3000 мсек, что должно быть достаточно. Пока что это таймауты. См. Вызов wait_for_completion_timeout() в wait_for_transfer().

Дело в том, что я получаю данные. Несмотря на то, что есть тайм-аут, мой буфер RX после того, как я получил тайм-аут, содержит действительные данные в нем, в любом случае для первого запуска теста. Во втором прогоне я получаю только все нулевые данные, а после rmmod и insmod драйвера снова получаю данные за один прогон. Как вы можете видеть в выводе dmesg, завершение callback sync_callback() никогда не вызывается. Он был назначен перед началом передачи в start_transfer() с помощью chan_desc->callback = sync_callback.

Что может ждать DMAEngine, чего не хватает, чтобы вызвать этот тайм-аут?

[ 6615.751516] dma_proxy module trying init...
[ 6615.757744] Allocating uncached memory at virtual address 0x00000000c589f4ff, physical address 0x000000003ae38fae
[ 6615.769511] Allocating uncached memory at virtual address 0x0000000045abc318, physical address 0x00000000450e2f46
[ 6615.779767] dma_proxy module init ok
[ 6623.936341] wait_for_transfer() - waiting for completion
[ 6627.085656] DMA timed out

Здесь код 3 функции (я думаю, наиболее подходящие) для этого неудачного теста, некоторые комментарии сокращены по сравнению с оригиналами для краткости. Обратите внимание, что в приложении для тестирования пользовательского пространства я отключил использование канала TX, который работает для начального теста обратной петли, но теперь FPGA отправляет данные сама. (Я все еще открываю его - так как по какой-то причине получение канала RX не удается, если я сначала не получаю канал TX)

static void sync_callback(void *completion)
{
    printk(KERN_INFO "sync_callback() before complete\n");
    complete(completion);
    printk(KERN_INFO "sync_callback() after complete\n");   
}
static void start_transfer(struct dma_proxy_channel *pchannel_p)
{
    enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
    struct dma_async_tx_descriptor *chan_desc;
    struct dma_proxy_channel_interface *interface_p = pchannel_p->interface_p;
    struct dma_device *dma_device = pchannel_p->channel_p->device;

    /* For now use a single entry in a scatter gather list just for future
     * flexibility for scatter gather. */
    sg_init_table(&pchannel_p->sglist, 1);
    sg_dma_address(&pchannel_p->sglist) = pchannel_p->dma_handle;
    sg_dma_len(&pchannel_p->sglist) = interface_p->length;

    chan_desc = dma_device->device_prep_slave_sg(pchannel_p->channel_p, &pchannel_p->sglist, 1,
                        pchannel_p->direction, flags, NULL);

    /* Make sure the operation was completed successfully */
    if (!chan_desc) {
        printk(KERN_ERR "dmaengine_prep*() error\n");
    } else {
        chan_desc->callback = sync_callback;
        chan_desc->callback_param = &pchannel_p->cmp;

        /* Initialize the completion for the transfer and before using it
         * then submit the transaction to the DMA engine so that it's queued
         * up to be processed later and get a cookie to track it's status */
        init_completion(&pchannel_p->cmp);

        pchannel_p->cookie = dmaengine_submit(chan_desc);
        if (dma_submit_error(pchannel_p->cookie)) {
            printk(KERN_INFO"Submit error\n");
            return;
        }

        /* Start the DMA transaction which was previously queued up in the DMA engine */
        dma_async_issue_pending(pchannel_p->channel_p);
    }
}
static void wait_for_transfer(struct dma_proxy_channel *pchannel_p)
{
    unsigned long timeout = msecs_to_jiffies(3000);
    enum dma_status status;

    pchannel_p->interface_p->status = PROXY_BUSY;

    printk(KERN_INFO "wait_for_transfer() - waiting for completion\n");

    /* Wait for the transaction to complete, or timeout, or get an error */
    timeout = wait_for_completion_timeout(&pchannel_p->cmp, timeout);
    status = dma_async_is_tx_complete(pchannel_p->channel_p, pchannel_p->cookie, NULL, NULL);

    if (timeout == 0)  {
        pchannel_p->interface_p->status  = PROXY_TIMEOUT;
        printk(KERN_ERR "DMA timed out\n");
    } else if (status != DMA_COMPLETE) {
        pchannel_p->interface_p->status = PROXY_ERROR;
        printk(KERN_ERR "DMA returned completion callback status of: %s\n",
               status == DMA_ERROR ? "error" : "in progress");
    } else
        pchannel_p->interface_p->status = PROXY_NO_ERROR;
}

Для справки, я ссылка на полный код трех задействованных исходных файлов:

Драйвер ядра:

Приложение для тестирования пользовательского пространства:

Добавлено : наличие переключателя в FPGA, который может маршрутизировать любой из обычных источников данных в ПЛИС или обратная петля данных, полученных в ПЛИС, обратно к ведомому устройству DMA, я попробовал "режим обратной петли" драйвера, чтобы затем убедиться, что у меня нет тайм-аута. Кажется, это просто работает, и я получаю верные данные. Хотя я также получаю обратно действительные данные, если я маршрутизирую из реального источника данных в FPGA, у меня есть тайм-аут. Интересно, в чем может быть разница ... То есть "почти работает" ...

...