当前位置:   article > 正文

nuc970 网络问题排查过程_nuc970 can

nuc970 can

1. 问题简介

     多台公设备重启过程中出现网络无法使用问题,使用ifconfig查看网络节点此时没有收到任何包,并且重启机器也存在无法恢复问题。

图片

图1.1 异常后ifconfig信息

2. 排查过程

  • 查看中断

    在PC中使用ping命令查看设备是否有反应,此时可以看到RJ45的数据灯在闪烁,说明此时是有数据进入的,通过查看 cat /proc/interrupts没有看到网络中断。

图片

图2.1 PC ping 设备

图片

图2.2 网络中断

    经过了解该问题之前就存在,解决办法是通过将eth0节点先down掉然后在up起来,出现问题后使用该方法进行测试,测试结果如下图所示,从下图可以看到第一次对eth0重启没有恢复,第二次时才恢复正常。从这点我们可以知道两三个信息,

    1)重启eth0不一定能恢复网络。

    2)重启eth0概率性可恢复,恢复后有提示。

    3)重启eth0 实际是对MAC进行操作,说明问题可能出现在MAC。

图片

图2.3 重启eth0 恢复网络

  • 查看提示信息

    通过错误提示找到其代码所在位置。打印在./net/netfilter/nf_conntrack_helper.c:215中,如下图所示,直接看代码不太能够理解其含义,首先需要知道一个整体的概念,不能钻到代码里面去,否则很容易不知道要干啥。

图片

图2.4 错误打印代码位置

从打印中可以看到两个关键词iptablesnf_conntrack,通过查阅资料可以了解到如下信息。

iptables:

netfilter/iptables(简称为iptables)组成Linux平台下的包过滤防火墙,与大多数的Linux软件一样,这个包过滤防火墙是免费的,它可以代替昂贵的商业防火墙解决方案,完成封包过滤、封包重定向和网络地址转换(NAT)等功能。

nf_conntrack:

nf_conntrack(在老版本的 Linux 内核中叫 ip_conntrack)是一个内核模块,用于跟踪一个连接的状态的。连接状态跟踪可以供其他模块使用,最常见的两个使用场景是 iptables 的 nat 的 state 模块。

    了解到了如上信息知道这个是防火墙的以及跟踪连接状态的,首先想起是否是由于这个防火墙将数据包给隔离了,所以通过配置的方式关闭了,iptablesnf_conntrack结果发现并没有用,网络还是存在异常。说明问题不在这部分。

  • RXERR 问题

    经过了解,最早该问题已经定位到了是RXERR的问题,RMII接口本身是不需要RXERR引脚,但NUC970的MAC的RXERR引脚不接入就会导致设备无法使用,具体原因是当启动过程中该引脚异常时候,此时NUC970的RXERR会进入错误状态,将MIEN寄存器的RXEN关掉。

    了解到这里知道读取相关寄存器地址,MAC0基地址EMACn_BA = 0xB0002000,

    EMACn_MIEN = EMACn_BA + 0x0AC = 0xB00020AC, 使用devmem读取寄存器值,具体如下:

  1. #异常时
  2. $devmem 0xB00020AC
  3. 0x01258C10
  4. $devmem 0xB00020b0
  5. 0x00800610
  6. #恢复正常后
  7. $devmem 0xB00020AC
  8. 0x01258C11
  9. devmem 0xB00020b0
  10. 0x00800000

    通过对比正常和异常的差异点,发现0xB00020AC的bit0在异常时候为0,该位描述如下,该位是控制接收中断使能的,若该位置0了则CPU将不会受到RX中断,由此可以和上面对应上,MAC0没有任何中断的原因就是因为RXIEN没有使能导致的。

接收中断启用控制RXIEN控制RX中断的产生。如果启用RXIEN,且RXINTR (EMACn_MISTA[0])值高,则EMAC对CPU产生RX中断。如果关闭RXIEN,即使设置任何状态位EMACn_MISTA[15:1],并使能相应的EMACn_MIEN,也不会对CPU产生RX中断。换句话说,如果S/W想从EMAC接收RX中断,这个位必须是启用。并且,如果S/W不想从EMAC接收任何RX中断,禁用此功能一些。

0 = RXINTR (EMACn_MISTA[0])被屏蔽,RX中断生成被禁用。1 = RXINTR (EMACn_MISTA[0]) is not mask and RX interrupt generation Enabled。

图片

图2.5 RXIEN寄存器描述

    基于上面我们尝试在异常的时候对RXIEN位使能,操作如下所示,当将该位置1后,此时网络恢复,使用ping命令可正常与PC通讯。说明问题就在这里。

$devmem 0xB00020AC w 0x01258C11

图片

图2.6 开启RX中断使能后网络恢复

到这里需要就有三个问题需要考虑。具体如下:

  • 什么情况下RXIEN会被禁止

  • 为什么通过down和up重启eth0不一定有用。

  • 若默认将RXIEN一直开启是否会影响网络

    带着这三个问题开始排查,首先查看新塘驱动看到3处和RXIEN相关的,分别是

    a. 使能mac 中断

  该函数是在open ether的时候开启的,用于打开MAC中断。

  1. static void nuc970_enable_mac_interrupt(struct net_device *dev)
  2. {
  3. unsigned int val;
  4. val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP | ENRDU;
  5. val |= ENTXBERR | ENRXBERR | ENTXABT | ENWOL;
  6. __raw_writel(val, REG_MIEN);
  7. }

   b. RX中断处理函数

    该函数中收到RX中断后会关闭RX中断,然后调用napi_schedule触发poll方法,然后再poll方法中处理网络数据,这样做的目的是减少硬件中断,避免消耗CPU资源。因为中断很多会使CPU一直陷入硬中断而没有时间处理别的事情。

  1. static irqreturn_t nuc970_rx_interrupt(int irq, void *dev_id)
  2. {
  3. struct net_device *dev = (struct net_device *)dev_id;
  4. struct nuc970_ether *ether = netdev_priv(dev);
  5. unsigned int status;
  6. struct platform_device *pdev = ether->pdev;
  7. nuc970_get_and_clear_int(dev, &status, 0xFFFF);
  8. dev_err(&pdev->dev, "zhuajian %s +%d status = 0x%x \n",__FUNCTION__, __LINE__, status);
  9. if (unlikely(status & MISTA_RXBERR)) {
  10. dev_err(&pdev->dev, "emc rx bus error\n");
  11. nuc970_reset_mac(dev, 1);
  12. } else {
  13. if(status & MISTA_WOL) {
  14. }
  15. if(status & MISTA_RXGD) {
  16. __raw_writel(__raw_readl(REG_MIEN) & ~ENRXINTR, REG_MIEN);
  17. napi_schedule(&ether->napi);
  18. }
  19. }
  20. return IRQ_HANDLED;
  21. }

    c. nuc970 poll函数

    网络数据接收,当数据接收完成或处理时间超了后会重新开启中断。

  1. static int nuc970_poll(struct napi_struct *napi, int budget)
  2. {
  3. int rx_cnt = 0;
  4. int complete = 0;
  5. struct platform_device *pdev = ether->pdev;
  6. rxbd = (ether->rdesc + ether->cur_rx);
  7. dev_err(&pdev->dev, "zhuajian %s +%d budget = 0x%x rx_cnt = 0x%x \n",__FUNCTION__, __LINE__, budget, rx_cnt);
  8. while(rx_cnt < budget) {
  9. if((rxbd->sl & RX_OWEN_DMA) == RX_OWEN_DMA) {
  10. complete = 1;
  11. break;
  12. }
  13. .......
  14. wmb(); // This is dummy function for ARM9
  15. rxbd->sl = RX_OWEN_DMA;
  16. if (++ether->cur_rx >= RX_DESC_SIZE)
  17. ether->cur_rx = 0;
  18. rxbd = (ether->rdesc + ether->cur_rx);
  19. }
  20. .......
  21. if(complete) {
  22. napi_complete(napi); //changed from __napi_complete(napi); by tanshi li for ifconfig eth0 down error
  23. __raw_writel(__raw_readl(REG_MIEN) | ENRXINTR, REG_MIEN);
  24. }
  25. rx_out:
  26. ETH_TRIGGER_RX;
  27. return(rx_cnt);
  28. }

         通过在三个函数中增加打印,发现异常时候进入了nuc970_rx_interrupt但是没有进入到poll中,也就是在nuc970_rx_interrupt关闭了中断,但由于没有进入到poll中所以无法重新开启中断,导致后续网络都是异常的。  

图片

图2.7 打印输出

    所以这里就要看为什么没有调用到nuc970_poll,理论上在nuc970_rx_interrupt中断中调用napi_schedule就会调用到nuc970_poll,所以现在就要看在什么情况下napi_schedule不会调用nuc970_poll。

    napi_schedule实际调用napi_schedule_prep判断该poll是否可被调度,经过测试发现异常时候就是在该函数返回异常,导致poll方法无法被调度,napi_schedule_prep的函数如下,主要是检测两个方面:

1、是否poll已经在调度

2、是否禁止了napi pending

  1. /**
  2. * napi_schedule_prep - check if napi can be scheduled
  3. * @n: napi context
  4. *
  5. * Test if NAPI routine is already running, and if not mark
  6. * it as running. This is used as a condition variable
  7. * insure only one NAPI poll instance runs. We also make
  8. * sure there is no pending NAPI disable.
  9. */
  10. static inline bool napi_schedule_prep(struct napi_struct *n)
  11. {
  12. return !napi_disable_pending(n) &&
  13. !test_and_set_bit(NAPI_STATE_SCHED, &n->state);
  14. }

    增加打印发现问题出在test_and_set_bit这里说明此时有其他poll方法被调度了,说明此时NAPI的状态中NAPI_STATE_SCHED位已经被设置。继续查看代码可以发现在eth0驱动加载时候会调用netif_napi_add,将poll增加到队列中。该函数就会设置状态为NAPI_STATE_SCHED,具体代码如下所示:

  1. static int nuc970_ether_probe(struct platform_device *pdev)
  2. {
  3. ....
  4. //增加napi 。
  5. netif_napi_add(dev, &ether->napi, nuc970_poll, /*16*/32);
  6. ether_setup(dev);
  7. if((error = nuc970_mii_setup(dev)) < 0) {
  8. dev_err(&pdev->dev, "nuc970_mii_setup err\n");
  9. goto err2;
  10. }
  11. error = register_netdev(dev);
  12. if (error != 0) {
  13. dev_err(&pdev->dev, "register_netdev() failed\n");
  14. error = -ENODEV;
  15. goto err2;
  16. }
  17. return 0;
  18. ....

        而在open函数中会调用napi_enable函数将NAPI_STATE_SCHED状态清除,这样做的目的是为了在驱动加载时候不工作,仅在用户ifconfig  up了网络节点时才正常工作。

    通过上面的描述和代码走读,绘制代码逻辑图如下:

图片

图2.8 驱动加载流程

图片

图2.9 网络数据处理简化流程

    通过走读代码发现问题出现在了open函数,由上可知在初始化时候调用了netif_napi_add,此时napi的状态是NAPI_STATE_SCHED,此时若有网络数据进来,则此时会触发RX中断,在中断中会关闭RX中断,且判断poll是否可被调度,此时判断状态为NAPI_STATE_SCHED,就不会调用到nuc970_poll,也就无法再开启网络中断。

    那为什么open的时候回导致进入中断了,查看open的代码如下,发现在nuc970_reset_mac 和nuc970_enable_mac_interrupt中都会开启RX使能,并且在reset_mac的时候会初始化rx中断,而前面也说了清除NAPI_STATE_SCHED是在open函数的napi_enable调用中做的,若还未调用到napi_enable就有数据来了,此时就会出现上一段所描述的场景。

  1. static int nuc970_ether_open(struct net_device *dev)
  2. {
  3. struct nuc970_ether *ether;
  4. struct platform_device *pdev;
  5. ether = netdev_priv(dev);
  6. pdev = ether->pdev;
  7. nuc970_reset_mac(dev, 0);
  8. nuc970_set_fifo_threshold(dev);
  9. nuc970_set_curdest(dev);
  10. nuc970_enable_cam(dev);
  11. nuc970_enable_cam_command(dev);
  12. nuc970_enable_mac_interrupt(dev);
  13. nuc970_set_global_maccmd(dev);
  14. ETH_ENABLE_RX;
  15. if (request_irq(ether->txirq, nuc970_tx_interrupt,
  16. 0x0, pdev->name, dev)) {
  17. dev_err(&pdev->dev, "register irq tx failed\n");
  18. return -EAGAIN;
  19. }
  20. if (request_irq(ether->rxirq, nuc970_rx_interrupt,
  21. IRQF_NO_SUSPEND, pdev->name, dev)) {
  22. dev_err(&pdev->dev, "register irq rx failed\n");
  23. free_irq(ether->txirq, dev);
  24. return -EAGAIN;
  25. }
  26. phy_start(ether->phy_dev);
  27. netif_start_queue(dev);
  28. napi_enable(&ether->napi);
  29. ETH_TRIGGER_RX;
  30. dev_info(&pdev->dev, "%s is OPENED\n", dev->name);
  31. return 0;
  32. }

    到这里根本原因已经非常清除了。就是新塘网络驱动的逻辑缺陷导致的。包括上面的三个问题也非常清楚了。

1、什么情况下RXIEN会被禁止

    中断来的时候就会被静止掉,这样做的目的是减少系统频繁处理中断,而导致无法去做其他事情。内核采用NAPI技术,当中断来时开启软中断,通过轮训的方式处理数据。

2、为什么通过down和up重启eth0不一定有用。

    在ifconfig eth0 up的时候调用的就是nuc970_open函数,若此时网络中有数据,例如有广播包的转发时候,此时就会出现关闭掉中断且不会调用到poll处理数据函数内。

3、若默认将RXIEN一直开启是否会影响网络

    会,若大量网络数据过来会导致CPU去处理中断而无法去处理数据。非常影响性能。

3. 问题验证:

    将nuc970_open函数中的不必要初始化去掉,并且将nuc970_reset_mac函数中的RX使能给去掉,将RX使能部分移动到napi_enable之后,修改后的代码如下:

图片

    图3.1 修复后的代码

修改后运行测试代码,测试代码思路是通过ping命令去ping网关,若成功则进行重启,若ping失败则不进行重启。此时就会卡住。当我们拷机测试出现设备部重启时就说明网络异常。

测试拿了两台设备,一台是最新的带PHY版本,一台是老的不带PHY版本,经过一天的测试网络数据均正常。每次都能够ping通,说明此时网络问题已修复。

带宽测试

    为了节约成本,这里测试两个版本的带宽和稳定性。通过在局域网下使用iperf3工具进行带宽测试,测试结果如下:

图片

    从测试结果看两者无任何差异,说明从带宽的角度两者并没有差异。若在抗干扰能力都一样的情况下,可以使用不带PHY的版本。

4. 总结

    此次排查过程了解了nuc970网络数据处理过程,同时也学习了NAPI的处理逻辑,对后续还是有非常大的帮助,同时也说明新塘SDK还是有缺陷的,后续使用过程中需要做好测试工作,将问题在产品开发阶段就暴露出来,避免问题产品流出。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读