ATH9K Driver Learning Part VI: Difference Between Packet Preparation And Transmission Control

第一种发包逻辑是

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_2link_3 中已经得到了很好的阐述,还请读者查阅。下一篇文章中笔者将介绍在发送过程中 ath9k 如何去调用 transmission rate adjustment functions。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值