资讯详情

资讯详情

QNX开发之ECAT专用网卡驱动ecpkt · 06-调试

QNX开发之ECAT专用网卡驱动ecpkt · 06-调试Iopkt原生网络框架的一大优势就是可以依赖OS提供的网络工具比如tcpdump/netstat等等对于ECAT调试来说分析通信报文是不折不扣的屠龙技通过分析报文可以解决ECAT使用中绝大多数的问题尤其通过对照TwinCAT报文对ECAT的调试有非常大的帮助。但是在ecpkt这里对于OS来说ecpkt并不属于通用的网络设备所以也无法使用OS提供的网络工具不过ecpkt里的报文数据完全是由ecpkt自己管理的我们可以针对ecpkt在开发一套抓包设施专门用来抓取ecpkt的通信报文进行分析。Iopkt下报文抓取大致可以分为以下几块通过BPF注入获取报文副本接收/发送使用libpcap封装BPF为应用层提供BPF配置和使用的接口应用层使用tcpdump工具通过libpcap获取报文副本并输出给用户我们在开发ecpkt下的抓包设施时可以仿照这个结构RM内额外新增采集端用来获取报文副本用户态开发ec_caplib对应libpcap封装用于报文抓取的用户接口用户程序ec_capdump对应tcpdump通过ec_caplib获取报文副本并输出另外ecpkt抓包时将报文仿照libpcap的格式进行存储这样就可以得到“.pcap”格式的报文文件这个报文文件可以直接在wireshark里打开进行分析就不用再专门开发报文解析的工具了。下面介绍具体的设计1. 抓包机制设计我们通过在RM里额外再创建一个设备节点/dev/ec_gem_cap来进行抓包使用这种方式有下面几点优势使用新设备节点可以支持poll系统调用兼容性更好通过设备fd隔离可能存在的多个用户态读者抓包能力从 /dev/ec_gem 的 ABI 里完全剥离。新节点与设备节点的整体框架如下另外消费者 cap_node.c 的 read 可以直接复用 /dev/ec_gem的延迟回复机制pending 数组 自脉冲 service_pending unblock 回收进一步减小工作量。2. 报文副本获取Iopkt框架下报文副本依靠BPF注入获取原生devnp驱动中已经包含有接收报文和发送报文的挂载点接收报文的挂载点在从bd里获得数据区地址后面发送报文的挂载点在数据区写入bd之后触发发送动作之前接收报文的挂载点比较清楚没有什么问题但是发送报文的挂载点其实有一些额外的考虑如上面所说发送报文的挂载点在数据区写入bd之后发送之前这里有个问题BPF抓包注入实际上就是将报文拷贝到副本的缓存空间里简单来说会有一次memcpy的调用这个memcpy的调用是一个纯软件的耗时操作而且是可以被抢占的那就意味着在这个memcpy的调用中可能会给报文发送引入额外的抖动那么在ECAT通信中就代表实时性降低这样看似乎把挂载点放到触发发送动作之后更合适可以尽量减少报文发送的抖动。实际上如果把挂载点放在发送之后会存在一个问题一般来说驱动中的网络报文发送都是可重入的比如ECAT中通常会把sdo交互和pdo交互分成单独的线程那么在驱动层面pdo报文和sdo报文同时到达的情况就不可避免如果我们把挂载点放在发送之后设想一种极端的场景sdo报文传输在 xzynq_transmit 之后、cap_push 之前被更高优先级的 PDO 交互打断PDO交互触发发送流程之前会遍历bdring释放完成发送的BD那么sdo报文对应的bd由于完成了发送会被直接回收然后这个bd可能世界分配给本轮pdo发送来使用那么当pdo完成发送驱动回到sdo报文处理流程来执行memcpy获取sdo报文副本时bd中的数据已经被替换掉了这样抓取到的报文就不对了。当然在上面的例子中主线的通信还是正常的只是抓包的报文不可靠了所以选在把发送报文副本获取的挂载点放在发送之前实际上是一种为了正确性牺牲实时性的妥协。注当前ecpkt的设计实际上是单write通道多个write操作实际上是在RM内部串行处理的不会触发上面的情况这里还是先采用在发送之前抓包的设计以便后面扩展RM为基于优先级的抢占式发送模式。3. 报文副本 frame_cap 的设计抓包副本用什么缓冲第一反应往往是「复用 frame_pool」——报文本来就在那里。但 frame_pool 是给 DMA 用的MAP_PHYS 承诺物理连续、PROT_NOCACHE 关掉 cache这两样对一份 DMA 引擎永远不碰的副本缓冲是纯开销——NOCACHE 让 CPU 每次读都穿 cache 到 DRAM物理连续限制了分配的灵活性。frame_cap 的环刻意不建在 frame_pool 上就是一片普通的 calloc() 可缓存内存CPU 写、CPU 读cache 只帮忙不碍事。环的布局slots × stride 的一维数组stride ROUND_UP(sizeof(rec) data_cap, 64)。rec 是 32 字节记录头——ready发布戳、ts_nsCLOCK_MONOTONIC 纳秒时间戳、dirTX/RX 方向、origlen线上原始长度、caplen实际存储长度——payload 紧随其后。默认 1024 槽 ×321536字节约 1.6 MBinit 时一次分配帧路径上零 malloc。并发模型是 MPSC多生产者TX dispatch 线程、RX worker 线程可能同时调 frame_cap_push单消费者。协议分三步第一步锁内预约——检查 head-tail slots满则 dropped 直接返回丢帧不阻塞帧路径否则拿走 seqhead临界区只有几条指令第二步锁外填数——memcpy payload 进自己预约到的槽这个槽在 tail 追上来之前只属于自己第三步发布盖章——release 语义原子写 readyseq1保证消费者看到匹配 ready 之前 payload 一定可见。消费侧的关键是「完整发布谓词」ready tail1 才算可读head tail 不够——生产者可能预约了序号但 payload 还没拷完。记录按序不可跳队头记录没发布完后面的即使发布完也要等顺序保证了 pcap 文件里的时间线。另一条纪律是「失败不消耗」take 时目标 buffer 不够大EMSGSIZE记录留在队头原地不动调用方换个大 buffer 重试不丢帧。消费者有两种形态二选一默认形态是模块内置的低优先级 writer 线程SCHED_RR prio 10低于 RX worker 和 iofunc把环里的记录搬到 PRINT 和/或 PCAP sink——自包含不需要客户端进程PULL 形态FRAME_CAP_FLAG_PULL不建线程、不建 sink环的拥有者自己调 frame_cap_ready() / frame_cap_take() 取帧——/dev/ec_gem_cap 的 cap_node 走的就是这条路客户端通过 read()/poll() 拿记录。两种消费者共用同一套生产者和环协议但绝不能同时存在——一个 head/tail 环只容得下一个 tail。与 frame_pool 的接缝在 ec_cap_push它从 frame_pool slot 里取数据frame_pool_data / frame_pool_len拷一份进 frame_cap 的环。两个子系统互相不知道对方存在——frame_pool 不需要懂抓包frame_cap 不需要懂 DMA。挂点挂上之后DMA 帧缓冲照样被 reap 回收、被下一帧复用而副本已经安全落在抓包环自己的内存里。以上就是在ecpkt下抓包设施的完整设计实测可以正确的获得支持wireshark的报文文件至此整个ecpkt的开发就全部完成了。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →