上一篇文章講了如何禁用載波偵聽(CSMA)和退避(BACKOFF)的方法,這一篇介紹如何禁用ACK。
禁用ACK主要分為三部分:
1. 在發送端設置不等待ACK回來就繼續發送;
2. 在接收端設置收到數據幀之后不返回ACK;
3. (可選)有些地方針對不等待ACK的情況會有WARN等處理措施,把它們去掉。
下面分別來看,首先在發送端設置不等待ACK就繼續發送,打開net/mac80211/tx.c,在ieee80211_tx_prepare函數中有這樣的判斷:
if (is_multicast_ether_addr(hdr->addr1)) { tx->flags &= ~IEEE80211_TX_UNICAST; info->flags |= IEEE80211_TX_CTL_NO_ACK; } else tx->flags |= IEEE80211_TX_UNICAST;
這兒是說,如果當前的目標地址是多播地址,則去掉單播的標志位,並設置不等到ACK,因為多播廣播是不需要ACK的嘛,如果不是多播,則加上單播的標志位,所以要在單播的時候也不等待ACK,只需要在else里面也加上IEEE80211_TX_CTL_NO_ACK就可以了:
if (is_multicast_ether_addr(hdr->addr1)) { tx->flags &= ~IEEE80211_TX_UNICAST; info->flags |= IEEE80211_TX_CTL_NO_ACK; } else { tx->flags |= IEEE80211_TX_UNICAST; info->flags |= IEEE80211_TX_CTL_NO_ACK; }
還有一個地方也有類似的控制,在net/mac80211/wme.c的ieee80211_set_qos_hdr中,設置QoS頭的時候有判斷:
if (is_multicast_ether_addr(hdr->addr1) || sdata->noack_map & BIT(tid)) { ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; info->flags |= IEEE80211_TX_CTL_NO_ACK; }
同樣是多播情況下設置禁用ACK,這里把if這個條件去掉,無論是不是多播都設置這兩行就可以了。
2. 第二步,在接收方設置不返回ACK幀,打開drivers/net/wireless/ath/ath9k/hw.c,在ath9k_hw_reset函數中找一個位置設置:
REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_ACK_DIS);
3. 這一步可選,有很多地方對沒有ACK的情況做了特殊處理,影響有大有小,先來看一個影響比較小的,net/mac80211/tx.c的ieee80211_tx_h_rate_ctrl函數中有:
if (WARN_ON_ONCE((info->control.rates[0].count > 1) && (info->flags & IEEE80211_TX_CTL_NO_ACK))) info->control.rates[0].count = 1;
如果禁用了ACK並且發送速率的最大嘗試次數大於1,則置為1,這里比較煩人的是這個WARN_ON_ONCE,本身它只打印一次堆棧信息,對性能影響不大,但是占用dmsg的輸出,而且有時候看着有警告什么的特別煩,所以還是把這里刪掉,最起碼把WARN_ON_ONCE去掉。
再來看一個,在速率調整算法獲取發送速率的時候,也就是get_rate函數開頭的位置,都會調用net/mac80211/rate.c的rc_no_data_or_no_ack_use_min函數,當然了,一旦禁用了ACK,速率調整算法就不能用了,不過有時候我們固定MCS發送的時候可能被這里困擾,因為在這個函數里:
return (info->flags & (IEEE80211_TX_CTL_NO_ACK | IEEE80211_TX_CTL_USE_MINRATE)) || !ieee80211_is_data(fc);
如果禁用了ACK,就會直接用傳統的低速率發送,根據當前修改驅動的目的,可能會被這個問題耽誤,注意一下就行,需要的時候把標紅的部分刪掉。