Я разрабатываю драйвер для доски beaglebone черного цвета.Водитель общается с FPGA.Когда у fpga есть данные для чтения, запускается IRQ.Поэтому я должен быстро запустить транзакцию DMA для чтения данных.Можно ли запустить транзакцию из атомарного контекста?
На данный момент я отправляю задачу в высокоприоритетную рабочую очередь, и она запускает транзакцию DMA, но иногда, когда я работаю на скорости 30 Мбит / с, я получаю большое значение (около 200-500).микросекунды) между вызовом queue_work
из обработчика irq и запуском задания из рабочей очереди.
Так можно ли запустить dma напрямую из irq или есть более быстрый способ запуска транзакции dma из обработчика irq?
Я использую ядро Linux 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();
}
Иногда (когда мы вставляем или удаляем флешку, когда какой-либо драйвер пишет сообщения в терминале), я получаюочень большая задержка между обработчиком irq и началом работы dma (~ 1 мс)