首页 > 其他分享 >eCos系统的lwIP驱动及lpc2xxx网卡驱动bug的解决办法

eCos系统的lwIP驱动及lpc2xxx网卡驱动bug的解决办法

时间:2022-11-21 10:06:48浏览次数:42  
标签:信号量 lwIP tx send 网卡 sc 驱动 eth


eCos系统的lwIP驱动存在一个bug,该bug导致用于数据发送同步的信号量计数值不断增长,当超出32位整数所能表示的最大值时将会从0xffffffff回滚到0,这时可能会导致lwIP协议栈核心线程永久挂起。


后果

该bug引起的可能后果包括:

 

  1. 覆盖前一次发送数据,这可能会导致在网络上出现错误的数据包,在TCP/IP中,这不会产生多大影响,因为上层协议会保证数据的完整性和重发机制。
  2. 长时间连续运行后可能导致lwIP的核心线程进入永久等待信号量状态,进入这个状态后,lwIP基本上不再会有任何响应。

 

解决办法

修改io/eth/<version>/src/lwip/eth_drv.c源文件的eth_drv_send函数,将319行的if ((sc->funs->can_send)(sc) < 1)删除。

修改前的代码

#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL
L318: // Wait until we can send
L319: if ((sc->funs->can_send)(sc) < 1)
L320: cyg_semaphore_wait(&sc->sc_arpcom.send_sem);
L321: if ((sc->funs->can_send)(sc) < 1)
L322: CYG_FAIL("cannot send packet");
#endif // CYGFUN_LWIP_MODE_SEQUENTIAL

修改代码前信号量计数器在不断增长

eCos系统的lwIP驱动及lpc2xxx网卡驱动bug的解决办法_eCos

修改后的代码

 

#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL
// Wait until we can send
cyg_semaphore_wait(&sc->sc_arpcom.send_sem);
if ((sc->funs->can_send)(sc) < 1)
CYG_FAIL("cannot send packet");
#endif // CYGFUN_LWIP_MODE_SEQUENTIAL

修改代码后,信号量计数值为1或0

eCos系统的lwIP驱动及lpc2xxx网卡驱动bug的解决办法_bug_02

lpc2xxx的硬件bug

LPC2XXX以及LPC176X等MCU的以太网控制器在硬件上存在一个bug,这个bug会导致系统复位后的第一个数据包发送完成后不会产生中断。在没有修复lwIP驱动之前,由于负负得正的神奇作用,这个bug不会导致失败,但是如果打开了eCos系统的断言选项,那么在将发送第二个数据包时引发断言;而在修复了lwIP驱动之后,这个bug反而起作用了,因此必须对lpc2xxx的硬件bug进行修复。如果仅修复lwIP驱动,那么lwIP将在尝试发送第二个数据包时进入永远等待信号量的状态;如果仅修复lpc2xxx,长时间运行后可能会进入永久等待信号量状态。

解决lpc2xxx的bug

lpc2xxx的bug仅在系统复位后第一次发送数据包时存在,因此修改lpc2xxx驱动的lpc2xxx_eth_send函数,如果是第一次发送数据包,那么做一次本来属于lpc2xxx_eth_deliver职责范围的工作——发送信号量。修改devs/eth/arm/lpc2xxx/<version>/src/if_lpc2xxx.c的lpc2xxx_eth_send函数。

修改前的代码

static void
lpc2xxx_eth_send(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list,
int sg_len,
int total_len,
unsigned long key)
{
L994: cyg_uint32 tx_producer_idx;
L995: cyg_uint32 tx_consumer_idx;
……
L1032: priv->cur_tx_key = key;
L1033: HAL_WRITE_UINT32(EMAC_TX_PROD_IDX, tx_producer_idx);
}

修改后的代码

 

static void
lpc2xxx_eth_send(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list,
int sg_len,
int total_len,
unsigned long key)
{
L994: static cyg_bool silicon_bug_fixed = false;
L995: cyg_uint32 tx_producer_idx;
L996: cyg_uint32 tx_consumer_idx;
……
L1033: priv->cur_tx_key = key;
L1034: HAL_WRITE_UINT32(EMAC_TX_PROD_IDX, tx_producer_idx);
L1035:
L1036: if(silicon_bug_fixed == false)
{
L1038: silicon_bug_fixed = true;
L1039: priv->tx_busy = false;
L1040: _eth_drv_tx_done(sc, priv->cur_tx_key, 0);
}
}

 

bug产生原因

eCos系统中的lwIP包含4部分代码,分别为lwIP协议栈核心(位于net/lwip_tcpip/<version>/src/core和net/lwip_tcpip/<version>/src/api)、lwIP协议栈与操作系统相关的部分(位于net/lwip_tcpip/<version>/src/ecos)、独立于硬件的驱动部分(位于io/eth/<version>/src/lwip)、硬件相关的驱动部分(位于devs/eth目录下,与具体使用的网卡控制器有关,这里以lpc2xxx的网卡控制器为例)。

lwIP有2种工作模式,一种为单线程模式,这种模式下所有协议栈核心代码及应用层代码必须在一个线程内;另一种为多线程模式,这种模式下协议栈核心将启动2个服务线程,应用层代码可以分布在不同的线程中。多线程模式更具有实用性,因此这里仅考虑多线程模式,单线程模式仅适用于网络功能简单且资源十分紧张的场合。

这里描述的bug存在于多线程模式下的独立于硬件的驱动程序中。在多线程模式下,eCos的lwIP驱动假设每次仅发送一个数据包,仅在前一个数据包发送完成后才能发送下一个数据包,这通过一个信号量来同步。lwIP协议栈核心线程调用eth_drv_send函数发送数据包并消费掉信号量。lwIP协议栈的另一个线程则处理以太网控制器的中断事件,当检测到数据发送完成时调用eth_drv_tx_done,eth_drv_tx_done则生产信号量。这是一个典型的生产者消费者模型,因此使用信号量是没有问题的,正常情况下该信号量的计数值将为0或1,不会出现其它数值,信号量初始化时计数值被初始化为1。问题在于生产者eth_drv_tx_done生产信号量是无条件的,也就是每次检测到数据包发送完成,都会生产信号量,而消费者eth_drv_send消费信号量是有条件的,仅在当前正有数据在发送时才会消费信号量,如果没有数据正在发送那么不会消费信号量,这导致了信号量的生产和消费是不平衡的,生产的数量会多于消费的数量。这种不平衡导致的结果是信号量的计数值不再是0或1,而是一个递增的数值。

修改前的代码

#ifdef CYGFUN_LWIP_MODE_SEQUENTIAL
L318: // Wait until we can send
L319: if ((sc->funs->can_send)(sc) < 1)
L320: cyg_semaphore_wait(&sc->sc_arpcom.send_sem);
L321: if ((sc->funs->can_send)(sc) < 1)
L322: CYG_FAIL("cannot send packet");
#endif // CYGFUN_LWIP_MODE_SEQUENTIAL

这最终导致一些问题,比较常见的情况是当sc->sc_arpcom.send_sem信号量的计数值大于1时且当前正在发送数据包且未发送完成的情况下又要调用eth_drv_send函数发送数据,L319判断控制器是否可发送数据,这时控制器正在发送数据,因此返回结果是不可以发送数据,也就是if条件成立,执行L320的信号量等待语句。当sc->sc_arpcom.send_sem信号量的计数值大于等于1时,信号量等待语句消费掉一个信号量然后立即返回,本来是需要等数据发送完成后再返回的,现在的情况是立即返回了。导致的结果是在有数据正在的发送的情况下,又硬塞进来一个发送请求,这可能会破坏正在发送的数据包完整性,最终导致发送了错误的包。从L321和L322可以看得出来,如果eCos系统的断言可用,那么将会触发L322的断言。

比较极端的情况下将导致调用eth_drv_send的lwIP核心线程处于永远挂起状态。信号量计数值的范围是有限制的,超过这个范围将会引起数值溢出归零,eCos信号量的计数值为32位无符号整数,有效范围为0~0xFFFFFFFF。假设执行到L319时信号量计数值已经被累加到0xFFFFFFFF,如果这时产生了数据发送完成中断,然后系统切换到lwIP的另一个线程,该线程将调用eth_drv_tx_done函数,eth_drv_tx_done函数调用信号量的post函数,post函数对信号量计数值进行累加,0xFFFFFFFF加1的结果是为0!再切回lwIP核心线程时,信号量计数值已经变成了0,因此L320的信号量等待将会挂起直到信号量计数值大于0,问题是信号量计数值只有在数据发送完成时才会进行累加,而发送数据的线程已经挂起等待信号量,死定了!先进行条件检测,某种条件下进行等待,这是条件变量的用法。

标签:信号量,lwIP,tx,send,网卡,sc,驱动,eth
From: https://blog.51cto.com/zoomdy/5872656

相关文章

  • 操作系统--设备驱动的抽象--磁盘
    rawdisk磁盘的第一层抽象:柱面,磁头,磁道,扇区<--->盘块;盘块的读写,耗费的时间主要在寻道上。加快寻道,相邻盘块相邻编址,可以从一次读一个扇区-->一次读一个盘块,虽然产生碎片......
  • 下载chrome驱动
    想要通过代码模拟chrome浏览器的使用,首先需要先安装chrome浏览器的驱动程序查看你的chrome浏览器的版本号点击链接,找到对应的版本(大版本号对上基本即可)https://chro......
  • NUCLEO-F042K6驱动的彩色灯环(WS2812)
    NUCLEO-F042K6驱动的彩色灯环(WS2812)NUCLEO-F042K6是STMicroelectronics出品的Nucleo系列开发板之一,MCU为Cortex-M0核心的STM32F042K6。WS2812是常用的集成驱动电路的16M色LE......
  • 领域驱动实施步骤
    战略设计会控制和分解战术设计的边界与粒度;战术设计则从实证角度验证领域模型的有效性、完整性和一致性,进而以演进的方式对战略设计进行迭代。在战略设计阶段,经过需求分析......
  • 领域驱动整体流程
    1.自下而上DDD自下而上的领域建模通常采用事件风暴,通过头脑风暴列出所有可能的业务行为和事件,然后找出产生这些行为的领域对象。通过事件风暴来梳理业务和抽象,在事件风暴......
  • lwIP协议栈timeouts->next->time赋值导致BusFault异常的解决办法
    所有调用了lwIPAPI的线程都应该使用lwIP的sys_thread_new来创建。mingdu.zhengatgmaildotcom解决办法所有调用了lwIPAPI的线程都应该使用lwIP的sys_thread_new来创建......
  • 树莓派(香橙派)通过.NET IoT 操作SPI编写屏幕驱动 顺手做个四足机器人(一)
    摘要这片文章主要是记录自己的整活过程,涉及到的技术包括.NETIoT,.NETWeb,.NETMAUI,框架采用的也是最新的.NET7。本人是用的树莓派Zero2W(ubuntu-22.04)进行开发测......
  • 遨博机械臂——末端工具ROS驱动
    文章目录​​知识目标​​​​1.机械臂末端工具(EOAT)​​​​2.电动夹爪​​​​3.气动吸盘​​​​参考​​知识目标学习机械臂常用末端工具构成;学习aubo机械臂安装电动......
  • 调整关于Opencore配置IMac没有声音输出驱动的问题
    前提:1,已经成功安装了黑苹果系统,并成功运行稳定了。2,遇到了插入了音响或耳机无法输出的问题。原理:通过Opencore来编辑EFI启动的配置文件。一是我使用的Opencore的版本;......
  • 领域驱动设计概念
    1.领域领域指的是范围或边界,在研究和解决业务问题时,DDD会按照一定的规则将业务领域进行细分。当领域被细分到一定程度后,DDD会将问题范围限定在特定的边界内(也就是领域),并在......