GA-X48-DQ6のネットワークが不調

症状としては突然ネットワークが切れる。
synergyを使用しているのでGA-X48-DQ6側のPCが使えなくなる。
仕方がないので一旦ログアウトしてから再起動すると問題なく使用できる。


この現象いままで時々発生していて気になってdmesgするとそれらしきエラーを発見。

[1690069.964641] sky2 eth2: hung mac 124:65 fifo 195 (58:53)
[1690069.964646] sky2 eth2: receiver hang detected
[1690069.964653] sky2 eth2: disabling interface
[1690069.964849] sky2 eth2: enabling interface
[1690069.968810] br0: port 1(eth2) entering disabled state
[1690073.007141] sky2 eth2: Link is up at 1000 Mbps, full duplex, flow control both
[1690073.007276] br0: port 1(eth2) entering learning state
[1690082.005050] br0: topology change detected, propagating
[1690082.005057] br0: port 1(eth2) entering forwarding state
[1690170.164642] br0: port 2(tap0) entering disabled state
[1690170.168197] device tap0 left promiscuous mode
[1690170.168201] br0: port 2(tap0) entering disabled state


ソースを取り合えず追ってみた。
Kernelはこんな感じ。

# uname -srvm
Linux 2.6.27-11-generic #1 SMP Thu Jan 29 19:24:39 UTC 2009 i686


ソースコードはdrivers/net/sky2.c。
上記のhung macの行はsky2_rx_hung()の中で出力されている。
この関数が呼ばれるルートは次の通り。
sky2_probe()->sky2_watchdog()->sky2_rx_hung()


sky2_probeはstruct pci_driver構造体の.probeに設定されている関数。
.probeはデバイスを初期化する為に呼び出す関数ポインタ。
まあどうでも良い。


問題はsky2_watchdog()->sky2_rx_hung()の流れ。
これは名前の通りWatchDog用の関数。
こんな感じで登録されている。

setup_timer(&hw->watchdog_timer, sky2_watchdog, (unsigned long) hw);
static void sky2_watchdog(unsigned long arg)
{
  struct sky2_hw *hw = (struct sky2_hw *) arg;

  /* Check for lost IRQ once a second */
  if (sky2_read32(hw, B0_ISRC)) {
    napi_schedule(&hw->napi);
  } else {
    int i, active = 0;

    for (i = 0; i < hw->ports; i++) {
      struct net_device *dev = hw->dev[i];
      if (!netif_running(dev))
        continue;
      ++active;

      /* For chips with Rx FIFO, check if stuck */
      if *1 {
        pr_info(PFX "%s: receiver hang detected\n",
          dev->name);
        schedule_work(&hw->restart_work);
        return;
      }
    }

    if (active == 0)
      return;
  }

  mod_timer(&hw->watchdog_timer, round_jiffies(jiffies + HZ));
}

static int sky2_rx_hung(struct net_device *dev)
{          
  struct sky2_port *sky2 = netdev_priv(dev);
  struct sky2_hw *hw = sky2->hw;
  unsigned port = sky2->port;
  unsigned rxq = rxqaddr[port];
  u32 mac_rp = sky2_read32(hw, SK_REG(port, RX_GMF_RP));
  u8 mac_lev = sky2_read8(hw, SK_REG(port, RX_GMF_RLEV));
  u8 fifo_rp = sky2_read8(hw, Q_ADDR(rxq, Q_RP));
  u8 fifo_lev = sky2_read8(hw, Q_ADDR(rxq, Q_RL));
      
  /* If idle and MAC or PCI is stuck */
  if (sky2->check.last == dev->last_rx &&
      ((mac_rp == sky2->check.mac_rp &&
        mac_lev != 0 && mac_lev >= sky2->check.mac_lev) ||
       /* Check if the PCI RX hang */
       (fifo_rp == sky2->check.fifo_rp &&
        fifo_lev != 0 && fifo_lev >= sky2->check.fifo_lev))) {
    printk(KERN_DEBUG PFX "%s: hung mac %d:%d fifo %d (%d:%d)\n",
           dev->name, mac_lev, mac_rp, fifo_lev, fifo_rp,
           sky2_read8(hw, Q_ADDR(rxq, Q_WP)));
    return 1;
  } else {
    sky2->check.last = dev->last_rx;
    sky2->check.mac_rp = mac_rp;
    sky2->check.mac_lev = mac_lev;
    sky2->check.fifo_rp = fifo_rp;
    sky2->check.fifo_lev = fifo_lev;
    return 0;
  }
}


コードとコメントから判断するにIRQをロストし、Rx用のFIFOチェックを行う。
その後、PCI RXのhangチェックで引っかかってエラーと。
チェック方法に問題がないのであれば本当にハード側のエラーなんだろうけど…。
ただ失敗した後、schedule_work()を呼んでネットワークの再起動を行っているみたいだけどこれが効いていないのも気になる。


ただこういうのって、自分の経験上ここに来るよりも前のレジスタ関係の扱いに問題がある場合もあったりする。
ハードの仕様書まで探す気力が無いからここでストップかな。
いっそのこと前回のようにLANカードを買ってくるのも1つの手かも。

*1:hw->flags & SKY2_HW_RAM_BUFFER) && sky2_rx_hung(dev