You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux循环DMA驱动FIFO满时读取到未就绪最新数据问题咨询

问题根源及修复方案

1 首先修复代码笔误

你当前logger_read函数存在明显的变量误用问题:从FIFO取出的缓冲区索引存在index变量中,但调用copy_to_user时错误使用了未赋值的period_index,这会直接导致读取的缓冲区地址错误,大概率就是你读到正在被DMA写入的缓冲区的核心原因。
修复方式:将copy_to_user对应的地址参数修改为pd->dest_dma_buffer[index * pd->transfer_size]。

2 现有逻辑的缺陷

除了笔误外,你的DMA处理逻辑还有三处设计问题:

  • 静态变量并发风险:dma_sync_callback中定义的previous_period_counter、period_counter是静态变量,只适用于单设备实例的场景,多个设备驱动实例会共享这两个变量导致计数混乱,建议将这两个变量移入fpgalogger_device结构体中随设备实例存储。
  • 信号量与FIFO计数不匹配:你仅在FIFO非满时调用up(&pd->sema),FIFO满时弹出旧元素插入新元素的操作不会更新信号量计数,会导致信号量的计数值与FIFO实际元素数量不一致,极端情况会出现FIFO有数据但read拿不到信号量、或者拿到信号量后FIFO为空的错误。
  • 缓冲区所有权逻辑混乱:你将FIFO大小设为和循环缓冲区总数一致,FIFO满时直接弹出最旧的缓冲区编号,相当于未经过读流程就直接把该缓冲区交还给DMA覆写,既会导致未读数据丢失,也不符合"缓冲区要么归DMA所有、要么归驱动读队列所有"的边界规则。

3 具体解决办法

根据你的业务需求二选一即可:

方案A(允许丢最新帧,保证已入队数据完整)

适合对数据连续性要求高,宁可丢新数据也不要旧数据错乱的场景:

  • 将FIFO大小设置为CYCLIC_DMA_PERIODS - 1,预留1个缓冲区永远归DMA控制,不会出现在FIFO中,从根源避免读写冲突。
  • 修改回调函数FIFO满的处理逻辑:如果FIFO已满,直接放弃插入新的缓冲区编号,不弹出旧元素,也不做其他操作,相当于丢弃最新完成的传输数据。
  • 保持信号量操作逻辑不变,保证FIFO元素数和信号量计数完全一致。

方案B(允许丢旧帧,永远保留最新数据)

适合需要优先保证最新数据可用的场景:

  • 同样将FIFO大小设置为CYCLIC_DMA_PERIODS - 1,预留1块DMA专属缓冲区。
  • 回调函数FIFO满时,弹出最旧的缓冲区编号同时调用down_trylock(&pd->sema)抵消对应的信号量计数,保证计数匹配,再插入新的缓冲区编号并调用up(&pd->sema)。
  • 弹出的旧缓冲区直接标记为可被DMA覆写即可。

4 额外优化建议

你在copy_to_user完成后立刻调用dma_sync_single_for_device的操作不必要,该操作应该在确认缓冲区已经可以交还给DMA写入的时候再调用,你当前的调用时机相当于提前把缓冲区控制权交回给DMA,也可能导致拷贝还没完全完成就被DMA覆写。

内容的提问来源于stack exchange,提问作者NonoxX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 00:24:02