Commit 0d528d85 authored by Felix Fietkau's avatar Felix Fietkau Committed by Johannes Berg

mac80211: improve the rate control API

Allow rate control modules to pass a rate selection table to mac80211
and the driver. This allows drivers to fetch the most recent rate
selection from the sta pointer for already buffered frames. This allows
rate control to respond faster to sudden link changes and it is also a
step towards adding minstrel_ht support to drivers like iwlwifi.

When a driver sets IEEE80211_HW_SUPPORTS_RC_TABLE, mac80211 will not
fill info->control.rates with rates from the rate table (to preserve
explicit overrides by the rate control module). The driver then
explicitly calls ieee80211_get_tx_rates to merge overrides from
info->control.rates with defaults from the sta rate table.
Signed-off-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 5de17984
......@@ -563,6 +563,9 @@ enum mac80211_rate_control_flags {
/* maximum number of rate stages */
#define IEEE80211_TX_MAX_RATES 4
/* maximum number of rate table entries */
#define IEEE80211_TX_RATE_TABLE_SIZE 4
/**
* struct ieee80211_tx_rate - rate selection/status
*
......@@ -659,6 +662,8 @@ struct ieee80211_tx_info {
s8 rts_cts_rate_idx;
u8 use_rts:1;
u8 use_cts_prot:1;
u8 short_preamble:1;
u8 skip_table:1;
/* 2 bytes free */
};
/* only needed before rate control */
......@@ -680,6 +685,8 @@ struct ieee80211_tx_info {
struct {
struct ieee80211_tx_rate driver_rates[
IEEE80211_TX_MAX_RATES];
u8 pad[4];
void *rate_driver_data[
IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)];
};
......@@ -1224,6 +1231,24 @@ enum ieee80211_sta_rx_bandwidth {
IEEE80211_STA_RX_BW_160,
};
/**
* struct ieee80211_sta_rates - station rate selection table
*
* @rcu_head: RCU head used for freeing the table on update
* @rates: transmit rates/flags to be used by default.
* Overriding entries per-packet is possible by using cb tx control.
*/
struct ieee80211_sta_rates {
struct rcu_head rcu_head;
struct {
s8 idx;
u8 count;
u8 count_cts;
u8 count_rts;
u16 flags;
} rate[IEEE80211_TX_RATE_TABLE_SIZE];
};
/**
* struct ieee80211_sta - station table entry
*
......@@ -1251,6 +1276,7 @@ enum ieee80211_sta_rx_bandwidth {
* notifications and capabilities. The value is only valid after
* the station moves to associated state.
* @smps_mode: current SMPS mode (off, static or dynamic)
* @tx_rates: rate control selection table
*/
struct ieee80211_sta {
u32 supp_rates[IEEE80211_NUM_BANDS];
......@@ -1264,6 +1290,7 @@ struct ieee80211_sta {
u8 rx_nss;
enum ieee80211_sta_rx_bandwidth bandwidth;
enum ieee80211_smps_mode smps_mode;
struct ieee80211_sta_rates __rcu *rates;
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
......@@ -1419,6 +1446,9 @@ struct ieee80211_tx_control {
* for different virtual interfaces. See the doc section on HW queue
* control for more details.
*
* @IEEE80211_HW_SUPPORTS_RC_TABLE: The driver supports using a rate
* selection table provided by the rate control algorithm.
*
* @IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF: Use the P2P Device address for any
* P2P Interface. This will be honoured even if more than one interface
* is supported.
......@@ -1451,6 +1481,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21,
IEEE80211_HW_AP_LINK_PS = 1<<22,
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23,
IEEE80211_HW_SUPPORTS_RC_TABLE = 1<<24,
IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25,
IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
};
......@@ -3136,6 +3167,25 @@ static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta,
void ieee80211_sta_set_buffered(struct ieee80211_sta *sta,
u8 tid, bool buffered);
/**
* ieee80211_get_tx_rates - get the selected transmit rates for a packet
*
* Call this function in a driver with per-packet rate selection support
* to combine the rate info in the packet tx info with the most recent
* rate selection table for the station entry.
*
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
* @sta: the receiver station to which this packet is sent.
* @skb: the frame to be transmitted.
* @dest: buffer for extracted rate/retry information
* @max_rates: maximum number of rates to fetch
*/
void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct sk_buff *skb,
struct ieee80211_tx_rate *dest,
int max_rates);
/**
* ieee80211_tx_status - transmit status callback
*
......@@ -4212,6 +4262,22 @@ bool rate_usable_index_exists(struct ieee80211_supported_band *sband,
return false;
}
/**
* rate_control_set_rates - pass the sta rate selection to mac80211/driver
*
* When not doing a rate control probe to test rates, rate control should pass
* its rate selection to mac80211. If the driver supports receiving a station
* rate table, it will use it to ensure that frames are always sent based on
* the most recent rate control module decision.
*
* @hw: pointer as obtained from ieee80211_alloc_hw()
* @pubsta: &struct ieee80211_sta pointer to the target destination.
* @rates: new tx rate set to be used for this station.
*/
int rate_control_set_rates(struct ieee80211_hw *hw,
struct ieee80211_sta *pubsta,
struct ieee80211_sta_rates *rates);
int ieee80211_rate_control_register(struct rate_control_ops *ops);
void ieee80211_rate_control_unregister(struct rate_control_ops *ops);
......
......@@ -156,6 +156,7 @@ struct ieee80211_tx_data {
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
struct ieee80211_key *key;
struct ieee80211_tx_rate rate;
unsigned int flags;
};
......
This diff is collapsed.
......@@ -48,15 +48,15 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
/* assume HW handles this */
if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
if (tx->rate.flags & IEEE80211_TX_RC_MCS)
return 0;
/* uh huh? */
if (WARN_ON_ONCE(info->control.rates[0].idx < 0))
if (WARN_ON_ONCE(tx->rate.idx < 0))
return 0;
sband = local->hw.wiphy->bands[info->band];
txrate = &sband->bitrates[info->control.rates[0].idx];
txrate = &sband->bitrates[tx->rate.idx];
erp = txrate->flags & IEEE80211_RATE_ERP_G;
......@@ -617,11 +617,9 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (void *)tx->skb->data;
struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
int i;
u32 len;
bool inval = false, rts = false, short_preamble = false;
struct ieee80211_tx_rate_control txrc;
struct ieee80211_sta_rates *ratetbl = NULL;
bool assoc = false;
memset(&txrc, 0, sizeof(txrc));
......@@ -653,10 +651,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
/* set up RTS protection if desired */
if (len > tx->local->hw.wiphy->rts_threshold) {
txrc.rts = rts = true;
txrc.rts = true;
}
info->control.use_rts = rts;
info->control.use_rts = txrc.rts;
info->control.use_cts_prot = tx->sdata->vif.bss_conf.use_cts_prot;
/*
......@@ -668,7 +666,9 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
if (tx->sdata->vif.bss_conf.use_short_preamble &&
(ieee80211_is_data(hdr->frame_control) ||
(tx->sta && test_sta_flag(tx->sta, WLAN_STA_SHORT_PREAMBLE))))
txrc.short_preamble = short_preamble = true;
txrc.short_preamble = true;
info->control.short_preamble = txrc.short_preamble;
if (tx->sta)
assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);
......@@ -692,16 +692,38 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
*/
rate_control_get_rate(tx->sdata, tx->sta, &txrc);
if (unlikely(info->control.rates[0].idx < 0))
return TX_DROP;
if (tx->sta && !info->control.skip_table)
ratetbl = rcu_dereference(tx->sta->sta.rates);
if (unlikely(info->control.rates[0].idx < 0)) {
if (ratetbl) {
struct ieee80211_tx_rate rate = {
.idx = ratetbl->rate[0].idx,
.flags = ratetbl->rate[0].flags,
.count = ratetbl->rate[0].count
};
if (ratetbl->rate[0].idx < 0)
return TX_DROP;
tx->rate = rate;
} else {
return TX_DROP;
}
} else {
tx->rate = info->control.rates[0];
}
if (txrc.reported_rate.idx < 0) {
txrc.reported_rate = info->control.rates[0];
txrc.reported_rate = tx->rate;
if (tx->sta && ieee80211_is_data(hdr->frame_control))
tx->sta->last_tx_rate = txrc.reported_rate;
} else if (tx->sta)
tx->sta->last_tx_rate = txrc.reported_rate;
if (ratetbl)
return TX_CONTINUE;
if (unlikely(!info->control.rates[0].count))
info->control.rates[0].count = 1;
......@@ -709,102 +731,6 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
(info->flags & IEEE80211_TX_CTL_NO_ACK)))
info->control.rates[0].count = 1;
if (is_multicast_ether_addr(hdr->addr1)) {
/*
* XXX: verify the rate is in the basic rateset
*/
return TX_CONTINUE;
}
/*
* Set up the RTS/CTS rate as the fastest basic rate
* that is not faster than the data rate unless there
* is no basic rate slower than the data rate, in which
* case we pick the slowest basic rate
*
* XXX: Should this check all retry rates?
*/
if (!(info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) {
u32 basic_rates = tx->sdata->vif.bss_conf.basic_rates;
s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0;
rate = &sband->bitrates[info->control.rates[0].idx];
for (i = 0; i < sband->n_bitrates; i++) {
/* must be a basic rate */
if (!(basic_rates & BIT(i)))
continue;
/* must not be faster than the data rate */
if (sband->bitrates[i].bitrate > rate->bitrate)
continue;
/* maximum */
if (sband->bitrates[baserate].bitrate <
sband->bitrates[i].bitrate)
baserate = i;
}
info->control.rts_cts_rate_idx = baserate;
}
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
struct ieee80211_tx_rate *rc_rate = &info->control.rates[i];
/*
* make sure there's no valid rate following
* an invalid one, just in case drivers don't
* take the API seriously to stop at -1.
*/
if (inval) {
rc_rate->idx = -1;
continue;
}
if (rc_rate->idx < 0) {
inval = true;
continue;
}
/*
* For now assume MCS is already set up correctly, this
* needs to be fixed.
*/
if (rc_rate->flags & IEEE80211_TX_RC_MCS) {
WARN_ON(rc_rate->idx > 76);
if (!(rc_rate->flags & IEEE80211_TX_RC_USE_RTS_CTS) &&
tx->sdata->vif.bss_conf.use_cts_prot)
rc_rate->flags |=
IEEE80211_TX_RC_USE_CTS_PROTECT;
continue;
}
if (rc_rate->flags & IEEE80211_TX_RC_VHT_MCS) {
WARN_ON(ieee80211_rate_get_vht_mcs(rc_rate) > 9);
continue;
}
/* set up RTS protection if desired */
if (rts)
rc_rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
/* RC is busted */
if (WARN_ON_ONCE(rc_rate->idx >= sband->n_bitrates)) {
rc_rate->idx = -1;
continue;
}
rate = &sband->bitrates[rc_rate->idx];
/* set up short preamble */
if (short_preamble &&
rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
rc_rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE;
/* set up G protection */
if (!rts && tx->sdata->vif.bss_conf.use_cts_prot &&
rate->flags & IEEE80211_RATE_ERP_G)
rc_rate->flags |= IEEE80211_TX_RC_USE_CTS_PROTECT;
}
return TX_CONTINUE;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment