淺談"watchdog timeout"出現的原因
[閱讀 329 次]
最近有比較多的人談到網卡的」watchdog timeout「問題,究竟是什麼原因造成的,大多數人都把網卡的性能不佳做為問題的根源所在。我認為網卡的性能只是一方面的因素,他還涉及到緩衝的大 小、單位時間內的包的數量、及網卡驅動程序等一系列因素。以下將從源代碼的角度來對他進行分析。首先,我們看看到底是哪個函數發出了「watchdog timeout」字符串,只要你查一下源代碼不難看出,在各網卡的驅動程序裡的XX_watchdog(XX是各網卡的名稱,如:8139是rl, AMD7990是pcn,Inter是fxp等等)函數發出的。函數比較簡單:
struct ifnet *ifp;
/*申明ifp是一個ifnet結構,結構存放了該網卡的輸入輸
出的函數指針和一些重要參數,當然也包括rl_watchdog函數的
指針*/
{
struct rl_softc *sc;
sc = ifp->if_softc;
/*ifnet是softc結構的一個子集,softc包含了更多的該網卡的參數*/
printf("rl%d: watchdog timeout\n", sc->rl_unit);
/*打印出是哪塊網卡出現問題。
sc->rl_unit代表該種網卡的第幾快。我們知道在一個機器裡同樣
的網卡可能有幾塊,當然此參數是由網卡驅動程序的初始化程序
填充*/
ifp->if_oerrors++;
/*累計輸出出現的錯誤包數量(o代表輸出)*/
rl_txeof(sc);
/*這裡是每個驅動程序不同的,此處為8139的,不過我覺得用rl_stop(sc)更好*/
rl_rxeof(sc);
/*這裡也是每個驅動程序不同。我覺得來一個rl_reset(sc)也不錯。*/
rl_init(sc);/*這裡大家都一樣,重新初始芯片。*/
return;
}
ifinit()
{
static struct timeout if_slowtim;
timeout_set(&if_slowtim, if_slowtimo, &if_slowtim);
/*簡單的說就是設置一定時器*/
if_slowtimo(&if_slowtim);
/*哈哈,等不急了,先調用了再說*/
}
if_slowtimo(arg)
void *arg;
{
struct timeout *to = (struct timeout *)arg;
struct ifnet *ifp;
int s = splimp();
/*在做以下操作的時候必須關中斷*/
TAILQ_FOREACH(ifp, &ifnet, if_list)
{/*搜索每一個接口設備,TAILQ_FOREACH實際上是for(...)*/
if (ifp->if_timer == 0 || --ifp->if_timer)
continue;
/*如果是if_timer為0或if_timer減1以後還為真,實際上是對每塊網卡
的計數器if_timer減1後判斷他是否還大於0,小於0就調用watchdog
函數。*/
if (ifp->if_watchdog) /*不過調用之前看看該卡有沒有watchdog函數*/
(*ifp->if_watchdog)(ifp);
}
splx(s);
timeout_add(to, hz /
IFNET_SLOWHZ);
/*每次計時器完成後都會清除,你不得不又加上去。hz是計算
機的主頻,就是說調度的間隔時間是和主頻成正比的關係。*/
}
struct ifnet *ifp;
{
struct rl_softc *sc;
struct mbuf *m_head = NULL;
sc = ifp->if_softc;
while(RL_CUR_TXMBUF(sc) == NULL) {/* 1:當輸出緩衝區為空時才進行新的輸出*/
IF_DEQUEUE(&ifp->if_snd, m_head);/*把將要輸出的數據加入到輸出隊列中。*/
if (m_head == NULL)/* 2:申請內存失敗*/
break;
if (rl_encap(sc, m_head))
{/*8139卡的弱智表現在此,多加一個頭部,還要長字節對齊,影響
到數據必須重新搬遷。花時間啊!*/
IF_PREPEND(&ifp->if_snd, m_head);
ifp->if_flags |= IFF_OACTIVE;
break;
}
if (ifp->if_bpf)/* 3:如果包過濾存在就進行過濾*/
bpf_mtap(ifp, RL_CUR_TXMBUF(sc));
CSR_WRITE_4(sc, RL_CUR_TXADDR(sc),/* 4:以下為硬件輸出的IO指令*/
vtophys(mtod(RL_CUR_TXMBUF(sc), caddr_t)));
CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc),
RL_TXTHRESH(sc->rl_txthresh) |
RL_CUR_TXMBUF(sc)->m_pkthdr.len);
RL_INC(sc->rl_cdata.cur_tx);
}
if (RL_CUR_TXMBUF(sc) !=
NULL)/*如果傳送緩衝不為空,說明數據放到緩衝中已經準備傳了*/
ifp->if_flags |= IFF_OACTIVE;/*加上正在傳標誌*/
ifp->if_timer = 5;/*設定計數器為5*/
return;
}
{
... 這中間我就不寫了
if ((status & RL_ISR_TX_OK) || (status & RL_ISR_TX_ERR))
/*如果中斷後狀態寄存器的標識 是成功或出錯,就調用下面的程序。*/
rl_txeof(sc);
...
}
再看rl_txeof:
static void rl_txeof(sc)
{
...
ifp->if_timer =
0;
/*哈哈,在這清0了,也就是說,只要你不是反覆在那傳,
不管傳輸錯誤和傳輸正確 都不會出現"watchdog timeout"*/
...
}
首先我們必須查出導致出現該問題的原因,即是這問題中的哪個引起的,我們來修改if.h中定義一全局變量:
在函數static void rl_start(ifp)中加入:
static void rl_start(ifp)
struct ifnet *ifp;
{
struct rl_softc *sc;
struct mbuf *m_head = NULL;
sc = ifp->if_softc;
u_int8_t tmperror;
if (RL_CUR_TXMBUF(sc) != NULL) {/*新加,如果是緩衝區不夠問題*/
myerror=1;
}
while(RL_CUR_TXMBUF(sc) == NULL) {
IF_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL)
{
myerror=2; /*內存分配出錯*/
break;
}
if (rl_encap(sc, m_head)) {
IF_PREPEND(&ifp->if_snd, m_head);
ifp->if_flags |= IFF_OACTIVE;
break;
}
if (ifp->if_bpf)
bpf_mtap(ifp, RL_CUR_TXMBUF(sc));
tmperror=myerror;/*在進行寫IO口前先保存前面出錯的原因*/
CSR_WRITE_4(sc, RL_CUR_TXADDR(sc),
vtophys(mtod(RL_CUR_TXMBUF(sc), caddr_t)));
CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc),
RL_TXTHRESH(sc->rl_txthresh) |
RL_CUR_TXMBUF(sc)->m_pkthdr.len);
myerror=tmperror;/*上面兩句沒問題的話再還原前面的出錯原因*/
RL_INC(sc->rl_cdata.cur_tx);
}
if (RL_CUR_TXMBUF(sc) != NULL)
ifp->if_flags |= IFF_OACTIVE;
ifp->if_timer = 5;
return;
}
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.