NETDEV WATCHDOG: ethx (xxx): transmit queue 0 timed out 分析

2024-03-22 20:10

本文主要是介绍NETDEV WATCHDOG: ethx (xxx): transmit queue 0 timed out 分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

platform: 高通 msm-3.18 kernel。 

mac: 高通ethernet mac。

phy: Ti dp83。

kernel log 如下:

[  451.647838] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[  462.010112] ------------[ cut here ]------------
[  462.013748] WARNING: CPU: 0 PID: 0 at net/sched/sch_generic.c:311 dev_watchdog+0x19c/0x284()
[  462.032953] NETDEV WATCHDOG: eth0 (qcom-emac): transmit queue 0 timed out
[  462.039654] Modules linked in: dp83tc811 qcom_emac xt_recent embms_kernel(O) ext4 jbd2 mbcache
[  462.048251] CPU: 0 PID: 0 Comm: swapper Tainted: G           O   3.18.120-perf #2
[  462.055782] [<c00147cc>] (unwind_backtrace) from [<c00127cc>] (show_stack+0x10/0x14)
[  462.063487] [<c00127cc>] (show_stack) from [<c0020328>] (warn_slowpath_common+0x64/0x84)
[  462.071556] [<c0020328>] (warn_slowpath_common) from [<c0020374>] (warn_slowpath_fmt+0x2c/0x3c)
[  462.080227] [<c0020374>] (warn_slowpath_fmt) from [<c0672314>] (dev_watchdog+0x19c/0x284)
[  462.088364] [<c0672314>] (dev_watchdog) from [<c0060e4c>] (call_timer_fn+0xa8/0x1ac)
[  462.096114] [<c0060e4c>] (call_timer_fn) from [<c0061558>] (run_timer_softirq+0x310/0x338)
[  462.104370] [<c0061558>] (run_timer_softirq) from [<c0023888>] (__do_softirq+0x1f0/0x2d4)
[  462.112521] [<c0023888>] (__do_softirq) from [<c0023c34>] (irq_exit+0x80/0xf0)
[  462.119700] [<c0023c34>] (irq_exit) from [<c005446c>] (__handle_domain_irq+0x68/0x90)
[  462.127546] [<c005446c>] (__handle_domain_irq) from [<c00087b4>] (gic_handle_irq+0x38/0x50)
[  462.135868] [<c00087b4>] (gic_handle_irq) from [<c0013154>] (__irq_svc+0x54/0x90)
[  462.143333] Exception stack(0xc0c49ee0 to 0xc0c49f28)
[  462.148338] 9ee0: 000003a9 00000000 00000cf8 000003e8 00000000 cdcb9810 cddecc10 c0c95128
[  462.156525] 9f00: 8d4fdf3b 83126e97 0019f1e0 00000000 00000000 c0c49f30 c0068000 c04d28b4
[  462.164687] 9f20: 000f0013 ffffffff
[  462.168139] [<c0013154>] (__irq_svc) from [<c04d28b4>] (lpm_cpuidle_enter+0x534/0x57c)
[  462.176062] [<c04d28b4>] (lpm_cpuidle_enter) from [<c04ce574>] (cpuidle_enter_state+0xcc/0x250)
[  462.184742] [<c04ce574>] (cpuidle_enter_state) from [<c004ae1c>] (cpu_startup_entry+0x240/0x2a0)
[  462.193514] [<c004ae1c>] (cpu_startup_entry) from [<c0bf2c0c>] (start_kernel+0x34c/0x3b8)
[  462.201670] ---[ end trace d242c284853cc116 ]---

先看下错误打印处的代码:

 由此函数的实现可知,有六个条件满足后才会打印该告警;分别是:

  1. NIC 的tx的队列不是no op队列(!disc_tx_is_noop());
  2. NIC 设备存在(netif_device_present());
  3. NIC 已经up (netif_running);
  4. NIC 的链路已通 (netif_carrier_ok());
  5. NIC 的发送功能已停止(netif_xmit_stopped());
  6. NIC 的发送功能停止已超过了NIC设置的超时时间(watchdog_timeo);

先说下结论:

该平台和该场景下触发该告警是由于mac驱动中的缺陷,在调整phy的link change时(adjust_link),关闭了mac的TX功能,且未及时重新打开TX的功能,也未通知kernel物理链路的未准备好(netif_carrier_off),而kernel检查到phy link up便一直发送数据,但未收到发送完成的反馈,导致被设置停止传输队列的标志,最终被看门狗检测到而打印该告警。


接着来看下netif_xmit_stopped函数是怎么判断发送功能已停止:

从 netif_xmit_stopped函数可知,是根据队列的state是否被设置了QUEUE_STATE_ANY_XOFF标志来判断发送功能是否已停止;接着查看QUEUE_STATE_ANY_XOFF的定义:

 从526行这里可知QUEUE_STATE_ANY_XOFF由QUEUE_STATE_DRV_XOFF和QUEUE_STATE_STACK_XOFF两个bit组成,而这两个bit 又由__QUEUE_STATE_DRV_XOFF和__QUEUE_STATE_STACK_XOFF两个标志组成;

在QUEUE_STATE_ANY_XOFF定义的下面几行能看到关于这两个标志的注释说明,该注释指出:

  1. __QUEUE_STATE_DRV_XOFF是驱动程序用来停止传输队列,netif_tx_*函数将操作该标志;
  2. __QUEUE_STATE_STACK_XOFF标志则是被堆栈用来单独停止传输队列;
  3. netif_xmit_*stopped函数来检查队列是否已停止(驱动程序或堆栈);
  4. 驱动程序应该只使用netif_tx_*函数而不要调用netif_xmit*_stopped函数;

接下来分别看看设置这两个标志的函数;

设置__QUEUE_STATE_DRV_XOFF标志的函数是 netif_tx_stop_queue函数:

由此可知__QUEUE_STATE_DRV_XOFF标志的设置没有其他条件;

设置__QUEUE_STATE_STACK_XOFF标志的函数是 netdev_tx_sent_queue函数

由此2573行可知,设置__QUEUE_STATE_STACK_XOFF标志,需要满足 dql_avail() < 0,接着看看netdev_tx_sent_queue函数被谁调用;

 从2602行可知该函数被netdev_sent_queue函数所封装,从注释可知,其作用是报告等待发送到网络设备硬件队列的字节数;接着分析dql_avail函数的实现;

 从dql_avail函数的注释可知,当队列超过限制值时才会返回小于0的值;

到此需要了解下dql是什么, Linux 网络设备子系统里的DQL;

由前面分析可知,dql_queued函数和dql_avail函数都是被netdev_tx_sent_queu函数调用,接下来查看下dql_completed函数的调用情况;

 从2612行可知,dql_completed函数被netdev_tx_completed_queue函数调用,且该函数会清除_QUEUE_STATE_STACK_XOFF标志;所以dql主要服务于netdev_tx_sent_queue和netdev_tx_completed_queue这两个函数,而这两个函数又分别被netdev_sent_queue函数和netdev_completed_queue函数所封装调用; 

从2636行到2637行的注释可知,这两个函数的上报字节数要匹配;

回到前面,设置__QUEUE_STATE_STACK_XOFF标志,需要满足 dql_avail() < 0 的情况,由前面关于dql的分析(Linux 网络设备子系统里的DQL)可知,当 总的排队的数量(num_queued) 大于了 调整后的队列极限值(adj_limit ) 时,才满足;其中 总的排队的数量(num_queued)在dql_queued函数中增加,在netdev_tx_sent_queue函数中被间接调用;而 调整后的极限值(adj_limit)则是在dql_completed函数中改变,在netdev_tx_completed_queue函数中被间接调用;所以存在该情况:netdev_tx_sent_queue函数有被调用,而netdev_tx_completed_queue函数没有被调用,导致 adj_limit 小于 num_queued,从而被设置 __QUEUE_STATE_STACK_XOFF标志 ;

从dql的初始化可知 adj_limit 的初始值是0而不是一个已定义常量值,所以还存在该情况:当第一次调用 netdev_tx_sent_queue时,由于 adj_limit 的初始值是0,所以 adj_limit 小于 num_queued,从而被设置 __QUEUE_STATE_STACK_XOFF标志 ;

总结下有两种情况会导致 adj_limit 小于 num_queued:

  1. netdev_tx_sent_queue 有被调用,netdev_tx_completed_queue 则没有被调用;
  2. 第一次调用 netdev_tx_sent_queue;

情况1很可疑,需要再分析 netdev_tx_completed_queue 的调用;情况2算是正常流程;从前面netdev_completed_queue 函数的2642行可知netdev_tx_completed_queue 函数被 netdev_completed_queue 封装了一层,从其注释可知其主要是上报设备完成的字节数;总结下这两个函数的情况:

  1. netdev_tx_sent_queue函数会调用dql_queued函数来增大num_queued,调用dql_avail函数来检查是否超过极限值;
  2. netdev_tx_completed_queue函数会调用dql_completed函数来调整adj_limit;
  3. netdev_tx_sent_queue函数会设置 __QUEUE_STATE_STACK_XOFF标志;
  4. netdev_tx_completed_queue函数会清除__QUEUE_STATE_STACK_XOFF标志;
  5. netdev_tx_sent_queue函数被netdev_sent_queue函数封装;
  6. netdev_tx_completed_queue函数被netdev_completed_queue函数封装;

接着查找下netdev_completed_queue函数的调用;

在emac_handle_tx函数中找的netdev_completed_queue的调用(842行);emac_handle_tx函数的注释表明其是处理tx的事件,其代码主要做了取消dma的映射,释放skb以及上报已发送完成的字节数;再接着分析emac_handle_tx函数被调用情形;

 可看到emac_handle_tx在emac_isr函数中调用,这是emac的中断处理函数,从其实现可知,只有发生了TX_PKT_INI中断才会调用emac_handle_tx;

 从高通soc datasheet 中的描述可知,当包传输完成时,才被触发该中断;

从前面到此的分析可知,是因为没有产生该中断,才间接导致 adj_limit 小于 num_queued (设置__QUEUE_STATE_STACK_XOFF标志,且发生超时);再往下分析没有产生该中断的原因。


以上是从代码的角度逆向分析的结果;接下来看看从正常的net device设备子系统到mac驱动发送数据的过程来正向分析下:

到此需要了解下net device设备子系统发送数据的过程;再顺便了解下mac驱动 发送数据的过程;

由前面的文章(mac驱动 发送数据的过程)可知,mac驱动发送数据前会上报要发送的字节数,发送完成后,会产生发送完成的中断,发送完成的中断里会上报已发送的包数和字节数;上报要发送的字节数的函数是netdev_sent_queue,上报已发送完成的字节数的函数则是netdev_completed_queue,而netdev_sent_queue函数会设置__QUEUE_STATE_STACK_XOFF标志,netdev_completed_queue函数则是会清除该标志,由前面的分析可知,设置了该标志,代表发送队列被关闭,也就会导致网络看梦超时;再总结下,会设置该标志的情况有以下两种:

  1. 第一次发送数据时,设置该标志;
  2. 发送队列的值达到最大值时,设置该标志;

第一种情况设置 该标志 后,只要在网络看门狗的超时时间内收到一次数据发送完成中断,就会清除该标志,因此该情况不易出现超时问题;而第二种情况才是主要的引发该问题的情况

由于netdevice子系统一直在发送数据包,但超过了当前发送队列的最大值后,仍未收到mac发送完成的中断,因此被设置了关闭发送队列标志,等到超过网络看门狗的timeout时间时,便打印该warning,然后调用mac驱动注册的网络看门狗超时处理函数;

理论及原理已分析完,接下来就是实战分析实际场景的问题:
为什么没有收到mac发送完成的中断。


一般以下几种可能:

  1. 该中断被禁用;
  2. 没有使能相应功能;
  3. 硬件损坏;

第一种情况,关闭mac中断的代码只存在mac的中断处理函数中(emac_isr函数的1212行),而且该中断处理函数退出前重新开启了mac的中断(emac_isr函数1267行),所以不太可能是该情况;
第三种情况,由于只是有时出现的问题,所以不是该情况;
第二种情况,从datasheet中找到了TX和RX使能的寄存器,以及TXQ和RXQ使能的寄存器,再从代码中找到了设置这些寄存器的函数,接着从调用该函数的地方,已能确定其存在关闭而未再打开该功能的情况;


可见寄存器默认情况下是没有使能RX和TX的;


 TXQ和RXQ寄存器默认情况下也是未使能的;


 emac_hw_start_mac函数的516行和520行分别是使能TXQ和RXQ;在526行和569行便是使能TX与RX;


 由emac_hw_stop_mac函数的594行到596行可见,其分别关闭了RXQ,TXQ,TX,RX功能;


由emac_hw_config_pow_save函数的259行和281行可知,TX与RX功能被关闭,以便节省电源;


分析导致第二种情况里产生的原因:

  • 1.分析函数的功能,及其被调用的关系
函数功能调用关系
emac_hw_start_mac使能了TX,RX 和 TXQ,RXQ只在emac_adjust_link函数里调用,且只有当phy的状态是link up时才会被调用
emac_hw_stop_mac关闭了TX,RX 和 TXQ,RXQ

在emac_adjust_link函数中当phy的状态是link down时被调用,以及

在emac_hw_reset_mac函数中调用;

emac_hw_config_pow_save关闭了TX,RX在emac_pm_suspend函数中被调用

从emac_adjust_link函数的1881行和1856行可知,emac_hw_start_mac函数只有当phy的状态是link up时才会被调用;从1888行可知,emac_hw_stop_mac函数在phy的状态是link down的时候会被调用;

从496行可知,emac_hw_stop_mac函数还被emac_hw_reset_mac函数调用;

由2975行可知,emac_hw_config_pow_save函数被emac_pm_suspend函数被调用;


 出现该问题(如题)时,并非处于休眠唤醒之后,所以不用考虑emac_hw_config_pow_save,由上可知emac_hw_stop_mac存在多个条件被调用,导致功能被关闭的情况;

  • 2.主要函数(emac_hw_stop_mac)被主动调用以及间接调用的情形:
标号调用关系调用情形
1)被emac_adjust_link函数主动调用当phy的状态是link down时被调用
2)

通过emac_hw_reset_mac函数,

被 emac_mac_down 函数间接调用

设置了EMAC_HW_CTRL_RESET_MAC标志便会调用
3)

通过emac_hw_reset_mac函数,

被emac_close 函数间接调用

设置了ADPT_STATE_DOWN标志便会调用,没设置则设置EMAC_HW_CTRL_RESET_MAC标志并调用emac_mac_down
4)

通过emac_hw_reset_mac函数,

被emac_pm_resume函数间接调用

唤醒时被调用
5)

通过emac_hw_reset_mac函数,

被函emac_probe数间接调用

mac驱动初始化时调用

由2052行可知, emac_hw_reset_mac函数,在 emac_mac_down 函数中当设置了EMAC_HW_CTRL_RESET_MAC标志时便会被调用;

 由2140行可知,emac_hw_reset_mac函数,在 emac_close 函数中,当设置了ADPT_STATE_DOWN标志便会被调用,而没设置ADPT_STATE_DOWN标志时,则设置EMAC_HW_CTRL_RESET_MAC标志并调用emac_mac_down函数,说明emac_close函数一定会间接调用emac_hw_stop_mac函数;

由3023行可知,emac_hw_reset_mac函数在emac_pm_resume函数中被调用;

由3320行可知,emac_hw_reset_mac函数在emac_probe函数中被调用;

由2. 1)可知,还需要分析emac_adjust_link被调用的情形;
由2. 2)3)可知,调用了emac_close函数,最终一定会调用emac_hw_stop_mac;而调用emac_close是由上层执行NIC down(ifconfig eth0 down/ifdown eth0)的操作时调用的;
由2. 4)可知,目前出现问题时,并非处于休眠唤醒之后,所以不用考虑;
由2. 5)可知,是驱动初始化时调用一次,此次还未up,无需使能mac发送接收功能,属于正常流程;


  • 3.关键函数emac_adjust_link的内部主要操作:
标号行段操作可能的结果
1)1824-1826

检查ADPT_TASK_LSC_REQ标志是否设置,

未设置直接返回,设置了则清除该标志

存在被PHY状态机调用时,但没有设置ADPT_TASK_LSC_REQ而直接返回的情况
2)

1835-

1836

检查ADPT_STATE_DOWN标志是否设置,

设置直接返回

防止在执行emac_close过程时,产生死锁
3)1839-1841

确保在该函数执行期间没有reset操作

(没有设置ADPT_STATE_RESETTING标志)

此处可能死锁
4)1851-1854更新link up状态
5)1881是link up则使能mac的发送接收功能
6)1888是link down则关闭mac的发送接收功能
7)1897-1898link发生改变则打印状态改变

结合2. 2)和3), 以及3. 6), 可知目前有以下几种情况会关闭mac的发送接收功能:

  • 执行ifconfig NIC down / ifdown NIC
  • phy状态机更新link status为link down

而目前只有以下一种情况会使能mac的发送接收功能:

  • phy状态机更新link status为link up

由此可知,必须由emac_adjust_link被调用,且必须设置了ADPT_TASK_LSC_REQ标志才能去 关闭或使能mac的发送接收功能,所以还得再分析ADPT_TASK_LSC_REQ标志被设置的情况;

  • 4.ADPT_TASK_LSC_REQ标志设置及清除的情形:
标号行段情形
1)1992在emac_mac_up函数中,phy_start之后被设置
2)283在emac_check_lsc函数中被设置
3)2042在emac_mac_down函数中,phy_stop之后被清除
4)1826在emac_adjust_link函数中被清除

由1)和3)可知,是为了让其在NIC up期间才允许emac_adjust_link执行且生效,而在NIC down期间不让其生效,此逻辑正常
由2)可知,mac检测到phy的link change而允许emac_adjust_link执行且生效,看似正常
由3.和4.结合起来,emac_adjust_link函数只被phy状态机调用,且该函数会清除ADPT_TASK_LSC_REQ标志,所以emac_adjust_link函数执行且生效只有以下两种情况:

  • 情况1:在NIC up后,未产生mac的phy link change中断的情况下,只会被允许执行一次(一次NIC有效的up,才会执行一次)(因为执行完ADPT_TASK_LSC_REQ标志已被清除)当第一次被phy状态机调用时,只会执行link up的change, 也就是emac_hw_start_mac(使能mac发送接收功能),因为phy状态机初始的phy link status值是link down;
  • 情况2:在NIC up后,产生过mac的phy link change中断的情况下,被允许执行一次,执行完后等待下一次产生mac的phy link change中断(可多次执行)

 实际发生过的该问题总共有四种情况,待分析emac_adjust_link函数的调用情形后,再来分析这四种情况,emac_adjust_link函数只在phy_state_machine函数中被调用;

  • 5.关键函数emac_adjust_link被phy_state_machine调用的情形:
标号行段状态操作
1)

804

PHY_AN读一次link status,link down或者自协商完成则调用
2)

841

PHY_NOLINK读一次link status,link up且自协商完成则调用
3)

857

PHY_FORCING读一次link status,再调用
4)

879

PHY_CHANGELINK读一次link status,再调用
5)

889

PHY_HALTEDlink status为link up时,再调用,并改成link down
6)937PHY_RESUMING读一次link status,再调用

 由1)和2)可知PHY_AN和PHY_NOLINK是根据条件限制才调用emac_adjust_link的,其他均是无条件直接调用的;

在该平台上,由于PHY是不支持自协商的,所以是关闭了自协商的功能,因此PHY的状态如下

情况状态
加载phy和mac驱动后的状态PHY_READY
NIC up时状态变化PHY_READY -> PHY_UP -> PHY_FORCING -> PHY_RUNNING/PHY_NOLINK
NIC down时状态变化PHY_RUNNING/PHY_NOLINK -> PHY_HALTED
PHY 从up到down时状态变化PHY_RUNNING -> 中断/PHY_CHANGELINK -> PHY_NOLINK
PHY 从down到up时状态变化PHY_NOLINK -> PHY_RUNNING


 

不论phy使用了中断还是轮询的方式,在该平台(高通3.18 kernel),NIC up之后,都是通过phy 状态机的PHY_FORCING状态下检测phy的link status,再调用adjust_link。phy的状态机,及emac_adjust_link函数的调用到此已经分析完,接下来根据实际情况的log来继续分析。

  • 6.从自测log以及其他人给的log中,实际发生过的该问题的四种情况如下:
标号前提条件操作及现象其他说明
1)NIC已经up,phy 也link up把NIC down掉(ifconfig eth0 down / ifdown eth0),断开phy的link up(断开phy mdi线束),再NIC up,再等待一会,看门狗timeout去掉了down/up NIC时卸载和加载驱动的操作
2)NIC已经up,phy 也link up短时间(1秒)内phy link发生多次变化(硬件自己产生的),再等待一会,看门狗timeout去掉了down/up NIC时卸载和加载驱动的操作
3)

NIC已经up,没有接对端(以太网转换器之类的)

phy link down

kernel突然打印phy link up,再等待一会,看门狗timeout正常的板子挂机
4)

NIC已经up,没有接对端(以太网转换器之类的)

phy link down

一会后再接上对端,phy link up,再等待一会,看门狗timeout正常的板子挂机


情况1的log如下:

 从1行到2行的打印可知当前phy状态机处于状态6(PHY_RUNNING),接下来将切换到状态9(PHY_CHANGELINK);从3行到6行可知dql在正常被调用,从7行可看到__QUEUE_STATE_STACK_XOFF标志被清掉,可知当前处于NIC,phy均up且正常的情况;从8行到17行可知,这里是上层执行了NIC down的操作,11行也表明phy状态机处于状态10(PHY_HALTED);13行到14行可见其已经调用了emac_hw_stop_mac函数将TX功能给关闭;18行到21行表明其NIC down了10秒左右后执行了NIC up的操作;23行到24行表明当前phy状态机处于状态4(PHY_UP),接下来将切换到状态8(PHY_FORCING);22行和25行表明kernel有在继续发送数据,且adj_limit不再增大;26行到32行表明phy状态机已经检查的phy link down了;从33行,40行,41行可知,kernel仍在继续发送数据,在41行可看出num_queued已经大于adj_limit(超过了极限值),在42看见已被设置了__QUEUE_STATE_STACK_XOFF标志,43行则是第一次检查到发送队列已停止,44行则是超过看门狗的超时时间后,发送队列还是已经停止,最终触发该告警;

情况1)分析:
NIC, phy均已up的前提下,down掉NIC,此时emac_hw_reset_mac函数已被调用(mac的发送接收功能已关闭),phy 状态机也已变成PHY_HALTED状态;这时候断开phy的link up(断开phy mdi线束),当再次up NIC,从log可看到kernel立即开始发送数据包到发送队列;而mac的发送接收功能已经在NIC down掉时关闭了,所以mac实际是没有发出数据包的,当最终超过了当前发送队列的最大值后,被设置了关闭发送队列标志(__QUEUE_STATE_STACK_XOFF),等到超过网络看门狗的timeout时间(5s),kernel打印该warning("eth0 (qcom-emac): transmit queue 0 timed out");

由此引出的疑问:
6.1.1 为什么down NIC之后再up NIC,kernel立即开始发送数据包到发送队列,即便kernel已经检测到phy link down,也没有停止发送数据包;而第一次up NIC时,kernel没有立即发送数据包到发送队列,而是等待检测到phy link up以后才开始发送数据包;

疑问——解答:
6.1.1 需要了解网络设备子系统发送数据包到发送队列(启动发送队列)的条件,在linkwatch_do_dev函数中可看出该条件是: 

  • NIC 设置了IFF_UP 标志(即是NIC up状态)
  • netif_carrier_ok(网络链路链接完整)(调用了netif_carrier_on函数)

从高通emac驱动中,没有找到netif_carrier_on函数的调用,但在emac_probe函数中找到一次netif_carrier_off函数的调用,在emac_probe函数中调用netif_carrier_off函数属于正常逻辑,说明高通平台emac主要是靠phy状态机来维护该状态;从phy状态机的PHY_FORCING状态可知,当其检测到phy link up时便会调用netif_carrier_on函数,接着就调用了emac_adjust_link函数;

结合前面所有分析可知,
当NIC第一次up,没有接上对端(以太网转换器,或没有接上phy的MDI线束),只是设置了IFF_UP 标志,所以网络设备子系统并没有发送数据包到发送队列(关闭发送队列),而当接上了对端(以太网转换器,或接上phy的MDI线束),phy状态机PHY_FORCING状态检测到phy link up时,便设置phy link status为up,并调用netif_carrier_on函数,通知kernel网络链路链接完整可以启动发送队列了;而NIC 已经up,phy 也已经up,正在发送数据包时,down 掉NIC再次up NIC,由于down操作里并不会调用netif_carrier_off函数,且该mac驱动中emac_close函数和emac_mac_down函数也未调用netif_carrier_off函数,也就是当前还是netif_carrier_ok的状态,当再次up NIC时,直接就满足了启动发送队列的两个条件,所以直接就开始发送数据包了,即使phy状态机已经检测到phy link down了,也还是在继续发送数据包;

解决方式:
在mac驱动中的emac_mac_down函数中,phy_stop函数前增加调用netif_carrier_off函数后,发现还存在其他情况,当正在执行NIC down的步骤时,在emac_mac_down函数中,执行到phy_stop函数前时,已经执行了netif_carrier_off函数,但由于还没关掉phy状态机,这时phy状态机检测到是phy link up状态,便又调用了netif_carrier_on函数,导致还是存在该问题;
正确做法:在emac_mac_down函数中,增加调用netif_carrier_off函数后,还需要在emac_mac_up函数中再调用netif_carrier_off函数;
经多次测试确定已修复该情况;


 情况2的log如下:

第1行表示产生了发送完成的中断(TX_PKT_INI),一直到第5行可看出当前是正常的NIC,phy均up的情况;6行表示产生了phy link down的中断,13行表示产生了phy link up的中断,18行和21行又分别再产生了一次phy link change的中断,从时间戳可知其在100毫秒内产生了4次phy link change的中断,从26的log可知,emac_adjust_link只执行了第一次phy link down的中断触发,然后调用了emac_hw_stop_mac函数来关闭了TX功能;从33行可知phy状态机检测到phy link up,34行phy状态机调用了netif_carrier_on函数来通知链路已准备好(这时kernel就会继续发数据了);36行可见phy状态机再次调用emac_adjust_link函数,但是该函数没有有效执行;38行到39行可见kernel又开始发送数据了,而39行已表明队列已超过极限值,40行便是打印设置__QUEUE_STATE_STACK_XOFF标志;最后47行打印该告警;

情况2)分析:
NIC, phy均已up的前提下,短时间(1秒)内phy link发生多次变化,mac也产生了相应的多次中断,但只有第一个变化link down被emac_adjust_link函数执行了,也就导致mac的发送接收功能被关闭(emac_hw_stop_mac),而最后一个变化是link up的中断,但emac_adjust_link函数没有生效,也就没有执行emac_hw_start_mac函数(mac的发送接收功能使能),而phy状态机检测到phy link up就已调用netif_carrier_on函数,kernel便开始发送数据包;但由于mac的发送接收功能已经在第一个变化link down时关闭了,所以mac没有发出数据包,最终超过了当前发送队列的最大值后,被设置了关闭发送队列标志(__QUEUE_STATE_STACK_XOFF),等到超过网络看门狗的timeout时间(5s),kernel打印该warning("eth0 (qcom-emac): transmit queue 0 timed out");

由此引出的疑问:
6.2.1 为什么硬件短时间内phy link产生多次变化;
6.2.2 为什么只有第一个变化link down被emac_adjust_link函数执行了,而最后一次变化link up没有被执行;

疑问——解答:
6.2.1 硬件短时间内(1s)产生多次link状态的变化,推测是在发送链接脉冲时受到干扰或抖动,导致产生该情况;即使硬件就是会这样,软件也还是要能处理,而当前的mac驱动显然没有处理该情况,这是软件的缺陷;
6.2.2 结合前面4.的2)和4)可知,第一个变化link down时,emac_check_lsc函数中设置了ADPT_TASK_LSC_REQ标志,所以emac_adjust_link执行并生效,emac_adjust_link函数执行后ADPT_TASK_LSC_REQ标志被清除;而最后一次变化link up没有被执行,尽管前面产生了多次中断,也是就设置了ADPT_TASK_LSC_REQ标志多次,但因为emac_adjust_link执行的太晚,在所有中断都执行完后(已经设置了ADPT_TASK_LSC_REQ标志),才刚被phy状态机调用,而且emac_adjust_link函数执行后把ADPT_TASK_LSC_REQ标志清除了,
后面没有再产生link change的中断,也就没有再设置ADPT_TASK_LSC_REQ标志;这时phy状态机再检测到phy link up,并设置netif_carrier_on函数,最后再调用emac_adjust_link函数;由于没有设置ADPT_TASK_LSC_REQ标志,所以最后一次emac_adjust_link函数调用没有生效;

解决方式:
在emac_adjust_link函数中不要清除ADPT_TASK_LSC_REQ标志;(因其会限制emac_adjust_link函数再需要时的未能有效执行)经实际测试后,在emac_adjust_link函数中存在死锁的问题,在以下情况复现:
当执行NIC down时,emac_close函数中已经设置了ADPT_STATE_RESETTING标志,emac_close函数还未执行完,phy状态机调用了emac_adjust_link函数,emac_adjust_link函数中存在的确保在该函数执行期间没有reset操作而死循环轮询直到没有设置ADPT_STATE_RESETTING标志时才退出的操作,而ADPT_STATE_RESETTING标志已经被emac_close函数中已经设置了,因此emac_adjust_link函数就死锁在这里了;
正确做法:在emac_adjust_link函数中不要清除ADPT_TASK_LSC_REQ标志,并在循环判断ADPT_STATE_RESETTING标志的循环中增加退出该循环的条件,即增加检查当ADPT_STATE_DOWN标志被设置则直接退出该函数;(因为已经在NIC down的流程了,不再需要phy状态机更新link状态)
经多次测试确定已修复该情况;


 情况3的log如下:

情况3)分析:
NIC已经up,不接对端(以太网转换器之类的),这样应该一直为phy link down状态,而kernel突然打印"eth0: link becomes ready"(phy link up),
说明是phy状态机检测到phy link up状态,便又调用了netif_carrier_on函数,而且打开了NIC的ipv6功能,才打印该消息("eth0: link becomes ready"),
而没有打印"Link is Up" / "Link is Down",说明mac驱动自己维护的phy link_up状态没有改变;从前面的log可看到调用了emac_mac_up函数,说明NIC up 后,phy状态机第一次调用emac_adjust_link函数时,是phy link down的状态(正确,本身就没有接对端),而后来突然phy状态机检测到phy link up了,而这时没有产生mac的phy link change中断,也就导致phy状态机调用emac_adjust_link函数没有生效执行,mac的发送接收功能
也就没打开(已经在第一次phy状态机调用emac_adjust_link函数link down时关闭了),所以mac没有发出数据包,最终超过了当前发送队列的最大值后,被设置了关闭发送队列标志,等到超过网络看门狗的timeout时间(5s),kernel打印该warning("eth0 (qcom-emac): transmit queue 0 timed out");

由此引出的疑问:
6.3.1 为什么没接对端(以太网转换器之类的),kernel还能检查到phy link up产生;
6.3.2 为什么phy状态机检测到phy link up了,而这时没有产生mac的phy link change中断;
6.3.3 怎么判断有没有产生mac的phy link change中断;

疑问——解答:
6.3.1 首先需要了解kernel是怎么判断phy link up的,phy状态机通过genphy_update_link函数来更新phy link status的,而该函数是通过两次读取phy的寄存器1,然后取第二次读的结果来判断bit 4是否是1来决定phy是link up 还是link down; 因为上层app也有在持续的读取phy的寄存器值,所以存在以下情况,上层app或kernel其他线程中有在读取phy的其他寄存器,而phy状态机也刚好在读取phy的寄存器,可能phy状态机读取phy的寄存器1的值刚好是其他寄存器的值,而且刚好bit 4为1,就被认为是phy link up了;
6.3.2 没有产生mac的phy link change中断,说明硬件上的确没有发生link change(毕竟都没接对端),而仅仅是phy状态机那一次读取的phy寄存器1的bit 4为1,而被设置phy link up;
6.3.3 如果产生mac了phy link change中断,就一定会调用emac_adjust_link函数,一定会打印"Link is Up" / "Link is Down";

解决方式:
要修复这种情况,只需在emac_mac_up函数中phy_start函数之后增加emac_hw_start_mac函数的调用即可;


 情况4的log如下:

情况4)分析:
NIC已经up,没有接对端(以太网转换器之类的),phy保持link down,一会后再接上对端,kernel打印"eth0: link becomes ready"(phy link up),
而没有打印"Link is Up" / "Link is Down",因为接上对端,从前面正常的log可确定一定会产生mac的phy link change中断的,而这里没有打印,那只有一种情况:接上对端后,短时间内多次中断,第一个中断是link down,且在执行emac_adjust_link函数之前多个中断都已经执行完,所以emac_adjust_link函数只会执行一次,而且是link down的;因为一开始没接对端,mac驱动维护的link_up是link down,因此执行了emac_adjust_link函数也没有打印"Link is Down"(没有发生变化);接着phy link status稳定下来保持为link up,此时phy状态机检测到phy link up,设置netif_carrier_on函数,但由于没有产生mac的phy link change 中断,所以不会执行emac_adjust_link函数(mac的TX功能也就保持关闭状态),所以mac没有发出数据包,最终超过了当前发送队列的最大值后,被设置了关闭发送队列标志,等到超过网络看门狗的timeout时间(5s),kernel打印该warning("eth0 (qcom-emac): transmit queue 0 timed out");

解决方式:
与情况3的修复方式一样;

注:情况1)和情况2)已经验证过; 情况3)和情况4)仅仅是从出问题的板子的log来分析推理的,真实原因或许不是推测的那样,但解决方式理论上是已解决该问题。

这篇关于NETDEV WATCHDOG: ethx (xxx): transmit queue 0 timed out 分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/836099

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

ActiveMQ—Queue与Topic区别

Queue与Topic区别 转自:http://blog.csdn.net/qq_21033663/article/details/52458305 队列(Queue)和主题(Topic)是JMS支持的两种消息传递模型:         1、点对点(point-to-point,简称PTP)Queue消息传递模型:         通过该消息传递模型,一个应用程序(即消息生产者)可以