nuc970 网络问题排查过程

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读取寄存器值,具体如下:

#异常时
$devmem 0xB00020AC 
0x01258C10
$devmem 0xB00020b0
0x00800610
#恢复正常后
$devmem 0xB00020AC
0x01258C11
devmem 0xB00020b0
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中断。

static void nuc970_enable_mac_interrupt(struct net_device *dev)
{
  unsigned int val;

  val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP | ENRDU;
  val |= ENTXBERR | ENRXBERR | ENTXABT | ENWOL;

  __raw_writel(val,  REG_MIEN);
}

   b. RX中断处理函数

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

static irqreturn_t nuc970_rx_interrupt(int irq, void *dev_id)
{
  struct net_device *dev = (struct net_device *)dev_id;
  struct nuc970_ether *ether = netdev_priv(dev);
  unsigned int status;
    struct platform_device *pdev = ether->pdev;

  nuc970_get_and_clear_int(dev, &status, 0xFFFF);

    dev_err(&pdev->dev, "zhuajian %s +%d status = 0x%x \n",__FUNCTION__, __LINE__, status);

  if (unlikely(status & MISTA_RXBERR)) {

    dev_err(&pdev->dev, "emc rx bus error\n");
    nuc970_reset_mac(dev, 1);

  } else {
    if(status & MISTA_WOL) {

    }

    if(status & MISTA_RXGD) {
      __raw_writel(__raw_readl(REG_MIEN) & ~ENRXINTR,  REG_MIEN);
      napi_schedule(&ether->napi);
    }
  }
  return IRQ_HANDLED;
}

    c. nuc970 poll函数

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

static int nuc970_poll(struct napi_struct *napi, int budget)
{
    int rx_cnt = 0;
  int complete = 0;
    struct platform_device *pdev = ether->pdev;

  rxbd = (ether->rdesc + ether->cur_rx);
    dev_err(&pdev->dev, "zhuajian %s +%d budget = 0x%x rx_cnt = 0x%x \n",__FUNCTION__, __LINE__, budget, rx_cnt);

  while(rx_cnt < budget) {

    if((rxbd->sl & RX_OWEN_DMA) == RX_OWEN_DMA) {
      complete = 1;
      break;
    }
  .......
      wmb();  // This is dummy function for ARM9
    rxbd->sl = RX_OWEN_DMA;

    if (++ether->cur_rx >= RX_DESC_SIZE)
      ether->cur_rx = 0;

    rxbd = (ether->rdesc + ether->cur_rx);

  }
 .......

  if(complete) {
    napi_complete(napi); //changed from __napi_complete(napi); by tanshi li for ifconfig eth0 down error
    __raw_writel(__raw_readl(REG_MIEN) | ENRXINTR,  REG_MIEN);
  }

rx_out:

  ETH_TRIGGER_RX;
  return(rx_cnt);
}

         通过在三个函数中增加打印,发现异常时候进入了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

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

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


static int nuc970_ether_probe(struct platform_device *pdev)
{
  ....
    //增加napi 。
  netif_napi_add(dev, &ether->napi, nuc970_poll, /*16*/32);

  ether_setup(dev);

  if((error = nuc970_mii_setup(dev)) < 0) {
    dev_err(&pdev->dev, "nuc970_mii_setup err\n");
    goto err2;
  }
 
  error = register_netdev(dev);
  if (error != 0) {
    dev_err(&pdev->dev, "register_netdev() failed\n");
    error = -ENODEV;
    goto err2;
  }
   
  return 0;
  ....

        而在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就有数据来了,此时就会出现上一段所描述的场景。

static int nuc970_ether_open(struct net_device *dev)
{
  struct nuc970_ether *ether;
  struct platform_device *pdev;

  ether = netdev_priv(dev);
  pdev = ether->pdev;

  nuc970_reset_mac(dev, 0);
  nuc970_set_fifo_threshold(dev);
  nuc970_set_curdest(dev);
  nuc970_enable_cam(dev);
  nuc970_enable_cam_command(dev);
  nuc970_enable_mac_interrupt(dev);
  nuc970_set_global_maccmd(dev);
  ETH_ENABLE_RX;


  if (request_irq(ether->txirq, nuc970_tx_interrupt,
            0x0, pdev->name, dev)) {
    dev_err(&pdev->dev, "register irq tx failed\n");
    return -EAGAIN;
  }

  if (request_irq(ether->rxirq, nuc970_rx_interrupt,
            IRQF_NO_SUSPEND, pdev->name, dev)) {
    dev_err(&pdev->dev, "register irq rx failed\n");
    free_irq(ether->txirq, dev);
    return -EAGAIN;
  }

  phy_start(ether->phy_dev);
  netif_start_queue(dev);
  napi_enable(&ether->napi);

  ETH_TRIGGER_RX;

  dev_info(&pdev->dev, "%s is OPENED\n", dev->name);

  return 0;
}

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

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还是有缺陷的,后续使用过程中需要做好测试工作,将问题在产品开发阶段就暴露出来,避免问题产品流出。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值