前言:從12年開始做無線驅動相關的工作,到13年大概做了一年半,現在歇了快一年了,以免白學那么久,最近重新整理了一下當時的資料,寫一點文章,這方面的帖子比較少,當時碰到過很多問題難以解決,我是用的linux2.6的內核,將來用其他版本的朋友也可能會碰到類似的問題,可以把我的解決方案做一個參考~主要內容是pktgen、iperf使用的注意事項、驅動禁用CSMA、BACKOFF、ACK的方法、速率調整算法源碼解讀等。后面的幾篇文章一一介紹,文章列表。
測試無線性能的pktgen有專用的版本,編譯atheros固件的時候會自帶一個無線版本的pktgen,我一開始用pktgen官網提供的pktgen發送腳本,怎么發都是一運行內核就panic,或者開發板變得非常卡,有時候沒有崩潰的情況下,dmsg查看輸出,可以看到源碼里這一句打印了堆棧信息:
WARN_ON(tid->ac->txq != txq);
代碼位置不重要,反正可以看到是txq,也就是發送隊列有問題。正巧呢經過仔細觀察pktgen的統計信息:
發現里面有queue_map_min和queue_map_max這樣兩個參數,pktgen官方的腳本里沒有設置,我從pktgen官網上下載的源碼里面也沒有提這兩個參數,這倆就是關鍵,現在的2是我后來自己設置的,默認好像是0。這個發送隊列在代碼里對應的是這樣的取值:
/** * enum ieee80211_ac_numbers - AC numbers as used in mac80211 * @IEEE80211_AC_VO: voice * @IEEE80211_AC_VI: video * @IEEE80211_AC_BE: best effort * @IEEE80211_AC_BK: background */ enum ieee80211_ac_numbers { IEEE80211_AC_VO = 0, IEEE80211_AC_VI = 1, IEEE80211_AC_BE = 2, IEEE80211_AC_BK = 3, };
這是數據包對應於不同接入類別的幾個發送隊列,正常數據包都是使用盡力而為這種級別,只要把queue_map_min和queue_map_max都設置為2,pktgen的數據包都用這個AC來發就沒問題了。
==================================分割線=========================================
關於這個發送隊列,實際在使用pktgen的過程中還有一個后續問題,在這里分享一下:
雖然設置了queue_map,但是不知道為什么,生成的前兩個數據包仍然是隊列0和1的,后面才是我指定的2的,雖然影響不大,但是有兩個弊端:一是pktgen的clone_skb域只能設置為0或其它較小的數,如果設置為10000,那么最前面的將近20000個包的發送隊列都是不對的。而clone_skb的值越大,pktgen發送包的效率越高,當然,clone_skb為0時的效率已經夠我們用的了,所以這一點關系不大。在這里解釋一下clone_skb這個屬性,pktgen調用驅動的某個函數(假設函數是tx())來進行發包,並把封裝數據的sk_buff對象(skb)作為參數傳進去,那么如果一共發三個包,大概就是這樣的流程(下面的都是偽代碼):
struct sk_buff *skb = get_new_skb();//生成一個新的skb並填充數據 tx(skb); struct sk_buff *skb = get_new_skb(); tx(skb); struct sk_buff *skb = get_new_skb(); tx(skb);
這是clone_skb=0或1的情況,也就是說,發送的這三個數據包是相互獨立的,沒有重復使用,如果clone_skb=2就指定兩個包共用一個skb,就變成了下面這樣:
struct sk_buff *skb = get_new_skb(); tx(skb); tx(skb); struct sk_buff *skb = get_new_skb(); tx(skb);
因為沒有重復生成skb,也就提高了效率。這是第一個問題,第二個是在驅動里有一開始提到的WARN_ON的那一步判斷,如果發送隊列不是預期的值,會打印堆棧信息,這個打印操作是很耗時,在內核中,過於頻繁的printk都可以導致系統崩潰,如果太頻繁的打印堆棧信息,也是會導致崩潰的(這也是不設置pktgen的發送隊列時板子可能會崩潰的原因),現在clone_skb的值是0,用腳本自動切換MCS時,每次使用新的MCS之后前兩個包都會打印出堆棧信息,而堆棧信息又很長,這樣很影響我們觀察在驅動其它地方使用printk打印的輸出。
解決方法是不管queue_map_min和queue_map_max怎么設置了,統一在數據包剛進入鏈路層代碼的地方強制把這個字段置成2,當然如果是以真實使用為目的還要改回去,現在用pktgen做實驗用,就沒關系了,pktgen發包是通過調用atheros驅動的
ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
這個函數,該函數在/net/mac80211/tx.c中,就在這個函數開始的位置判斷當前數據包是不是pktgen的數據包(通過pktgen頭部的magic number來進行判斷),如果是,就調用前面提到的skb_set_queue_mapping函數把它放到2號隊列里去。代碼片段如下:
if (skb->len > 58){ __le32 *magic = &skb->data[42]; if(*magic == 0xbe9be955) /* magic number of pktgen */ skb_set_queue_mapping(skb, 2); /* BE */ }
pktgen的最小包長是14(MAC頭)+20(IP頭)+8(UDP頭)+16(Pktgen頭),大於該值才進行判斷,pktgen頭部是從第42字節開始,按小端序取出來和pktgen的magic number來進行比較,如果匹配,則設置發送隊列。