第一种发包逻辑是
ath9k_tx (main.c) -> ath9k_tx_start (xmit.c) -> ath_tx_prepare (xmit.c)
-> ath_tx_send_normal (xmit.c) -> ath_tx_txqaddbuf (xmit.c) ->
ath9k_hw_txstart (mac.c)
这一套流程恰恰就是笔者在 Learning Part III 中总结过的,ath9k driver 将上层传下来的包放入 hardware buffer 进行发送的过程。第二种方法就着重于 transmission tasklet and interrupts。
ath9k_tasklet() (main.c) -> ath_tx_edma_tasklet() (xmit.c) ->
ath_tx_process_buffer (xmit.c) -> ath_tx_complete_aggr (xmit.c) (if ampdu
is used) -> ath_tx_complete_buf (xmit.c) -> ath_tx_complete (xmit.c)
ath_txq_skb_done (xmit.c)
由上面的流程可见,ath9k driver 会对已经发送出去的 packet 进行一系列处理,例如释放内存。那么 ath9k driver 是如何调整发送速率的呢?这里面其实应用了 ath9k transmission rate adjustment 功能。众所周知,在 802.11 通信协议下,发送端会基于信道状况来动态的调整自己的 transmission rate。因为 802.11 通信协议大多采取 CSMA/CA 机制,如果一个 station 在明知道信道繁忙的情况下还不降低自己的 transmission rate,那么就只会增加信道干扰进而极大地降低 network throughput。在本篇文章中,笔者汇总了 ath9k transmission rate adjustment 功能中涉及的结构体和 functions,下一篇文章中再讨论 transmission tasklet and interrupts。
Transmission adjustment: Structures
在 transmission adjustment process 中发挥重要作用的结构体汇总如下:
ieee80211_sta
struc ieee80211_sta 储存了发送对象的基本信息,包括它支持的频率段(决定了最高 transmission rate),mac 地址和 capacibility。
/include/net/mac80211.h
/**
* struct ieee80211_sta - station table entry
*
* A station table entry represents a station we are possibly
* communicating with. Since stations are RCU-managed in
* mac80211, any ieee80211_sta pointer you get access to must
* either be protected by rcu_read_lock() explicitly or implicitly,
* or you must take good care to not use such a pointer after a
* call to your sta_remove callback that removed it.
*
* @addr: MAC address
* @aid: AID we assigned to the station if we're an AP
* @supp_rates: Bitmap of supported rates (per band)
* @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *), size is determined in hw information.
*/
struct ieee80211_sta {
u32 supp_rates[IEEE80211_NUM_BANDS]; //supported frequency bands
u8 addr[ETH_ALEN];
u16 aid;
struct ieee80211_sta_ht_cap ht_cap;
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};
struct ath_rate_priv
这个 structure 定义了这台设备的 priv (private?) rate control information。
/drivers/net/wireless/ath/ath9k/rc.h
/**
* struct ath_rate_priv - Rate Control priv data
* @state: RC state
* @probe_rate: rate we are probing at
* @probe_time: msec timestamp for last probe
* @hw_maxretry_pktcnt: num of packets since we got HW max retry error
* @max_valid_rate: maximum number of valid rate
* @per_down_time: msec timestamp for last PER down step
* @valid_phy_ratecnt: valid rate count
* @rate_max_phy: phy index for the max rate
* @per: PER for every valid rate in %
* @probe_interval: interval for ratectrl to probe for other rates
* @ht_cap: HT capabilities
* @neg_rates: Negotatied rates
* @neg_ht_rates: Negotiated HT rates
*/
struct ath_rate_priv {
u8 rate_table_size;
u8 probe_rate;
u8 hw_maxretry_pktcnt;
u8 max_valid_rate;
u8 valid_rate_index[RATE_TABLE_SIZE];
u8 ht_cap;
u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX];
u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE];
u8 rate_max_phy;
u8 per[RATE_TABLE_SIZE];
u32 probe_time;
u32 per_down_time;
u32 probe_interval;
struct ath_rateset neg_rates;
struct ath_rateset neg_ht_rates;
const struct ath_rate_table *rate_table;
struct dentry *debugfs_rcstats;
struct ath_rc_stats rcstats[RATE_TABLE_SIZE];
};
ieee80211_tx_info
这个 structure 定义在一个 packet 的 control buffer 里面,可见其重要性。ieee80211_tx_info 结构相对复杂,包含 control & status units,对 TX Condition 控制和分析有非常重要的作用。对这个结构体的详细分析,请参考 link_2.
/include/net/mac80211.h
/**
* struct ieee80211_tx_info - skb transmit information
*
* This structure is placed in skb->cb for three uses:
* (1) mac80211 TX control - mac80211 tells the driver what to do
* (2) driver internal use (if applicable)
* (3) TX status information - driver tells mac80211 what happened
*
* The TX control's sta pointer is only valid during the ->tx call,
* it may be NULL.
*
* @flags: transmit info flags, defined above
* @band: the band to transmit on (use for checking for races)
* @antenna_sel_tx: antenna to use, 0 for automatic diversity
* @pad: padding, ignore
* @control: union for control data
* @status: union for status data
* @driver_data: array of driver_data pointers
* @ampdu_ack_len: number of acked aggregated frames.
* relevant only if IEEE80211_TX_STAT_AMPDU was set.
* @ampdu_len: number of aggregated frames.
* relevant only if IEEE80211_TX_STAT_AMPDU was set.
* @ack_signal: signal strength of the ACK frame
*/
struct ieee80211_tx_info {
/* common information */
u32 flags;
u8 band;
u8 antenna_sel_tx;
/* 2 byte hole */
u8 pad[2];
union {
struct {
union {
/* rate control */
struct {
struct ieee80211_tx_rate rates[
IEEE80211_TX_MAX_RATES];
s8 rts_cts_rate_idx;
};
/* only needed before rate control */
unsigned long jiffies;
};
/* NB: vif can be NULL for injected frames */
struct ieee80211_vif *vif;
struct ieee80211_key_conf *hw_key;
struct ieee80211_sta *sta;
} control;
struct {
struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
u8 ampdu_ack_len;
int ack_signal;
u8 ampdu_len;
/* 15 bytes free */
} status;
struct {
struct ieee80211_tx_rate driver_rates[
IEEE80211_TX_MAX_RATES];
void *rate_driver_data[
IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)];
};
void *driver_data[
IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)];
};
};
ieee80211_tx_rate
这个 structure 简单直接。它在 transmission 开始前定义了这个 packet 可以在不同的 transmission rate 上最多重传几次。在 transmission 结束之后,ath9k driver 会根据收发情况去改写 ieee80211_tx_rate 中的数值从而表明这个 packet 是在哪一个 transmission rate 上发送几次之后才成功的。更加详细的介绍还请参考link_2.
/include/net/mac80211.h
/**
* struct ieee80211_tx_rate - rate selection/status
*
* @idx: rate index to attempt to send with
* @flags: rate control flags (&enum mac80211_rate_control_flags)
* @count: number of tries in this rate before going to the next rate
*
* A value of -1 for @idx indicates an invalid rate and, if used
* in an array of retry rates, that no more rates should be tried.
*
* When used for transmit status reporting, the driver should
* always report the rate along with the flags it used.
*
* &struct ieee80211_tx_info contains an array of these structs
* in the control information, and it will be filled by the rate
* control algorithm according to what should be sent. For example,
* if this array contains, in the format { <idx>, <count> } the
* information
* { 3, 2 }, { 2, 2 }, { 1, 4 }, { -1, 0 }, { -1, 0 }
* then this means that the frame should be transmitted
* up to twice at rate 3, up to twice at rate 2, and up to four
* times at rate 1 if it doesn't get acknowledged. Say it gets
* acknowledged by the peer after the fifth attempt, the status
* information should then contain
* { 3, 2 }, { 2, 2 }, { 1, 1 }, { -1, 0 } ...
* since it was transmitted twice at rate 3, twice at rate 2
* and once at rate 1 after which we received an acknowledgement.
*/
struct ieee80211_tx_rate {
s8 idx;
u8 count;
u8 flags;
} __packed;
ieee80211_tx_rate_control
这个 structure 广泛的应用于 rc algortithms (rc.c)。他详细的规定了除了 transmission rate 以外的,控制过程中十分重要的 structures。
/include/net/mac80211.h
/**
* struct ieee80211_tx_rate_control - rate control information for/from RC algo
*
* @hw: The hardware the algorithm is invoked for.
* @sband: The band this frame is being transmitted on.
* @bss_conf: the current BSS configuration
* @reported_rate: The rate control algorithm can fill this in to indicate
* which rate should be reported to userspace as the current rate and
* used for rate calculations in the mesh network.
* @rts: whether RTS will be used for this frame because it is longer than the
* RTS threshold
* @short_preamble: whether mac80211 will request short-preamble transmission
* if the selected rate supports it
* @max_rate_idx: user-requested maximum rate (not MCS for now)
* (deprecated; this will be removed once drivers get updated to use
* rate_idx_mask)
* @rate_idx_mask: user-requested rate mask (not MCS for now)
* @skb: the skb that will be transmitted, the control information in it needs
* to be filled in
* @bss: whether this frame is sent out in AP or IBSS mode
*/
struct ieee80211_tx_rate_control {
struct ieee80211_hw *hw;
struct ieee80211_supported_band *sband;
struct ieee80211_bss_conf *bss_conf;
struct sk_buff *skb;
struct ieee80211_tx_rate reported_rate;
bool rts, short_preamble;
u8 max_rate_idx;
u32 rate_idx_mask;
bool bss;
};
Transmission adjustment: Functions
重点介绍三个 functions: ath_rate_init、ath_get_rate、ath_tx_status。笔者的学习基于这篇博客: link_3。 另外介绍一下发送端是如何确认一个 packet 有没有被成功接受的方法
ath_rate_init
该 function 的作用为起始化 rate control 算法,source code 如下:
/ath9k/rc.c
static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta)
{
struct ath_softc *sc = priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_rate_priv *ath_rc_priv = priv_sta;
int i, j = 0;
u32 rate_flags = ieee80211_chandef_rate_flags(&sc->hw->conf.chandef);
for (i = 0; i < sband->n_bitrates; i++) {
if (sta->supp_rates[sband->band] & BIT(i)) {// Check which rates are supported by hardware
if ((rate_flags & sband->bitrates[i].flags)
!= rate_flags)
continue;
ath_rc_priv->neg_rates.rs_rates[j]
= (sband->bitrates[i].bitrate * 2) / 10;
j++;
}
}
ath_rc_priv->neg_rates.rs_nrates = j;
if (sta->ht_cap.ht_supported) {// Which mcs rates can be supported, add them to ath_rc_priv
for (i = 0, j = 0; i < 77; i++) {
if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
ath_rc_priv->neg_ht_rates.rs_rates[j++] = i;
if (j == ATH_RATE_MAX)
break;
}
ath_rc_priv->neg_ht_rates.rs_nrates = j;
}
ath_rc_priv->rate_table = ath_choose_rate_table(sc, sband->band,
sta->ht_cap.ht_supported);
if (!ath_rc_priv->rate_table) {
ath_err(common, "No rate table chosen\n");
return;
}
ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta);
ath_rc_init(sc, priv_sta);
}
ath_get_rate
这个 function 用来在传输结束后去改写 structure ieee80211_tx_rate 中的内容。
/ath9k/rc.h
static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
struct ieee80211_tx_rate_control *txrc)
// This function does not transmit a packet, it just sets re-transmit count etc to the ieee80211_tx_rate structure
// for future transmissions
{
struct ath_softc *sc = priv;
struct ath_rate_priv *ath_rc_priv = priv_sta;
const struct ath_rate_table *rate_table;
struct sk_buff *skb = txrc->skb; // txrc structure includes the packet will be transmitted
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *rates = tx_info->control.rates;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
__le16 fc = hdr->frame_control;
u8 try_per_rate, i = 0, rix;
int is_probe = 0;
if (rate_control_send_low(sta, priv_sta, txrc)) //rate_control_send_low - helper for drivers for management (no destination) /no-ack packets
/*Rate control algorithms that agree to use the lowest rate to send management frames and NO_ACK data with
the respective hw retries should use this in the beginning of their mac80211 get_rate callback. If true is
returned the rate control can simply return. If false is returned we guarantee that sta and sta and
priv_sta is not null.
*/
return;
/*
* For Multi Rate Retry (MRR) we use a different number of
* retry attempt counts. This ends up looking like this:
*
* MRR[0] = 4 ( If the packet is a probe, set MRR[0] just attempts 4 times in the ieee80211_tx_rate structure.)
* MRR[1] = 4
* MRR[2] = 4
* MRR[3] = 8
*
*/
try_per_rate = 4;
rate_table = ath_rc_priv->rate_table;
rix = ath_rc_get_highest_rix(ath_rc_priv, &is_probe); //1. determine whether it is a probe or not
//2. Based on the PER,determines the current
// maximized data rate as rix.
if (conf_is_ht(&sc->hw->conf) &&
(sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
tx_info->flags |= IEEE80211_TX_CTL_LDPC;
if (conf_is_ht(&sc->hw->conf) &&
(sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
tx_info->flags |= (1 << IEEE80211_TX_CTL_STBC_SHIFT);
if (is_probe) {
/*
* Set one try for probe rates. For the
* probes don't enable RTS.
*/
ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
1, rix, 0); // Write ieee80211_tx_rate structure (rates) based on txrc
/*
* Get the next tried/allowed rate.
* No RTS for the next series after the probe rate.
*/
ath_rc_get_lower_rix(ath_rc_priv, rix, &rix);
ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
try_per_rate, rix, 0);
tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
} else {
/*
* Set the chosen rate. No RTS for first series entry.
*/
ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
try_per_rate, rix, 0); // If the packet is not probe, then try 4 times for every available rate
}
for ( ; i < 4; i++) {
/*
* Use twice the number of tries for the last MRR segment.
*/
if (i + 1 == 4)
try_per_rate = 8;
ath_rc_get_lower_rix(ath_rc_priv, rix, &rix);
/*
* All other rates in the series have RTS enabled.
*/
ath_rc_rate_set_series(rate_table, &rates[i], txrc,
try_per_rate, rix, 1); // 1 is rtsctsenable, used as a boolean, Set USE_RTS_CTS=1
// RTS/CTS (Request To Send / Clear To Send) CSMA/CA
}
/*
* NB:Change rate series to enable aggregation when operating
* at lower MCS rates. When first rate in series is MCS2
* in HT40 @ 2.4GHz, series should look like:
*
* {MCS2, MCS1, MCS0, MCS0}.
*
* When first rate in series is MCS3 in HT20 @ 2.4GHz, series should
* look like:
*
* {MCS3, MCS2, MCS1, MCS1}
*
* So, set fourth rate in series to be same as third one for
* above conditions.
*/
if ((sc->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) &&
(conf_is_ht(&sc->hw->conf))) {
u8 dot11rate = rate_table->info[rix].dot11rate;
u8 phy = rate_table->info[rix].phy;
if (i == 4 &&
((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) ||
(dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) {
rates[3].idx = rates[2].idx;
rates[3].flags = rates[2].flags;
}
}
/*
* Force hardware to use computed duration for next
* fragment by disabling multi-rate retry, which
* updates duration based on the multi-rate duration table.
*
* FIXME: Fix duration
*/
if (ieee80211_has_morefrags(fc) ||
(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) {
rates[1].count = rates[2].count = rates[3].count = 0;
rates[1].idx = rates[2].idx = rates[3].idx = 0;
rates[0].count = ATH_TXMAXTRY;
}
ath_rc_rate_set_rtscts(sc, rate_table, tx_info);
}
ath_tx_status & ath_rc_tx_status
ath_tx_status 首先判断该 packet 是否含有 status info。如果有,使用 ath_rc_tx_status 去读出 status info。
/ath9k/rc.c
static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb)
{
struct ath_softc *sc = priv;
struct ath_rate_priv *ath_rc_priv = priv_sta;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
__le16 fc = hdr->frame_control;
if (!priv_sta || !ieee80211_is_data(fc))
return;
/* This packet was aggregated but doesn't carry status info */
if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
!(tx_info->flags & IEEE80211_TX_STAT_AMPDU))
return;
if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED)
return;
ath_rc_tx_status(sc, ath_rc_priv, skb);
/* Check if aggregation has to be enabled for this tid */
if (conf_is_ht(&sc->hw->conf) &&
!(skb->protocol == cpu_to_be16(ETH_P_PAE))) {
if (ieee80211_is_data_qos(fc) &&
skb_get_queue_mapping(skb) != IEEE80211_AC_VO) {
u8 *qc, tid;
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & 0xf;
if(ath_tx_aggr_check(sc, sta, tid))
ieee80211_start_tx_ba_session(sta, tid, 0);
}
}
}
/ath9k/rc.c
static void ath_rc_tx_status(struct ath_softc *sc,
struct ath_rate_priv *ath_rc_priv,
struct sk_buff *skb)
{
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *rates = tx_info->status.rates;
struct ieee80211_tx_rate *rate;
int final_ts_idx = 0, xretries = 0, long_retry = 0;
u8 flags;
u32 i = 0, rix;
for (i = 0; i < sc->hw->max_rates; i++) { // Determine the transmission rate in which the packet is transmitted
rate = &tx_info->status.rates[i];
if (rate->idx < 0 || !rate->count)
break;
final_ts_idx = i;
long_retry = rate->count - 1;
}
if (!(tx_info->flags & IEEE80211_TX_STAT_ACK))
xretries = 1; // xretries=1, means that the data frame has not been successfully transmitted
/*
* If the first rate is not the final index, there
* are intermediate rate failures to be processed.
*/
if (final_ts_idx != 0) {
for (i = 0; i < final_ts_idx ; i++) {
if (rates[i].count != 0 && (rates[i].idx >= 0)) {
flags = rates[i].flags;
/* If HT40 and we have switched mode from
* 40 to 20 => don't update */
if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
!(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG))
return;
rix = ath_rc_get_rateindex(ath_rc_priv, &rates[i]);
ath_rc_update_ht(sc, ath_rc_priv, tx_info,
rix, xretries ? 1 : 2,
rates[i].count);
}
}
}
flags = rates[final_ts_idx].flags;
/* If HT40 and we have switched mode from 40 to 20 => don't update */
if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
!(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG))
return;
rix = ath_rc_get_rateindex(ath_rc_priv, &rates[final_ts_idx]);
ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry); // This function is actually used for updating PER
ath_debug_stat_rc(ath_rc_priv, rix);
}
Conclusion
本篇文章着重归纳了 ath9k 中的 transmission rate adjustment functions,但并没有很详尽地介绍细节。原因是这些内容在 link_2 和 link_3 中已经得到了很好的阐述,还请读者查阅。下一篇文章中笔者将介绍在发送过程中 ath9k 如何去调用 transmission rate adjustment functions。