Commit 942741da authored by Felix Fietkau's avatar Felix Fietkau Committed by Johannes Berg

wifi: mac80211: switch airtime fairness back to deficit round-robin scheduling

This reverts commits 6a789ba6 and
2433647b.

The virtual time scheduler code has a number of issues:
- queues slowed down by hardware/firmware powersave handling were not properly
  handled.
- on ath10k in push-pull mode, tx queues that the driver tries to pull from
  were starved, causing excessive latency
- delay between tx enqueue and reported airtime use were causing excessively
  bursty tx behavior

The bursty behavior may also be present on the round-robin scheduler, but there
it is much easier to fix without introducing additional regressions
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
Acked-by: default avatarToke Høiland-Jørgensen <toke@toke.dk>
Link: https://lore.kernel.org/r/20220625212411.36675-1-nbd@nbd.nameSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 7f884baa
......@@ -6832,6 +6832,9 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
{
}
void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq, bool force);
/**
* ieee80211_schedule_txq - schedule a TXQ for transmission
*
......@@ -6844,7 +6847,11 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
* The driver may call this function if it has buffered packets for
* this TXQ internally.
*/
void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
static inline void
ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
__ieee80211_schedule_txq(hw, txq, true);
}
/**
* ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
......@@ -6856,8 +6863,12 @@ void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
* The driver may set force=true if it has buffered packets for this TXQ
* internally.
*/
void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool force);
static inline void
ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool force)
{
__ieee80211_schedule_txq(hw, txq, force);
}
/**
* ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
......
......@@ -1581,38 +1581,6 @@ static void sta_apply_mesh_params(struct ieee80211_local *local,
#endif
}
static void sta_apply_airtime_params(struct ieee80211_local *local,
struct sta_info *sta,
struct station_parameters *params)
{
u8 ac;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
struct airtime_sched_info *air_sched = &local->airtime[ac];
struct airtime_info *air_info = &sta->airtime[ac];
struct txq_info *txqi;
u8 tid;
spin_lock_bh(&air_sched->lock);
for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
if (air_info->weight == params->airtime_weight ||
!sta->sta.txq[tid] ||
ac != ieee80211_ac_from_tid(tid))
continue;
airtime_weight_set(air_info, params->airtime_weight);
txqi = to_txq_info(sta->sta.txq[tid]);
if (RB_EMPTY_NODE(&txqi->schedule_order))
continue;
ieee80211_update_airtime_weight(local, air_sched,
0, true);
}
spin_unlock_bh(&air_sched->lock);
}
}
static int sta_apply_parameters(struct ieee80211_local *local,
struct sta_info *sta,
struct station_parameters *params)
......@@ -1811,8 +1779,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta_apply_mesh_params(local, sta, params);
if (params->airtime_weight)
sta_apply_airtime_params(local, sta, params);
sta->airtime_weight = params->airtime_weight;
/* set the STA state after all sta info from usermode has been set */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
......
......@@ -216,14 +216,14 @@ static ssize_t aql_txq_limit_read(struct file *file,
"VI %u %u\n"
"BE %u %u\n"
"BK %u %u\n",
local->airtime[IEEE80211_AC_VO].aql_txq_limit_low,
local->airtime[IEEE80211_AC_VO].aql_txq_limit_high,
local->airtime[IEEE80211_AC_VI].aql_txq_limit_low,
local->airtime[IEEE80211_AC_VI].aql_txq_limit_high,
local->airtime[IEEE80211_AC_BE].aql_txq_limit_low,
local->airtime[IEEE80211_AC_BE].aql_txq_limit_high,
local->airtime[IEEE80211_AC_BK].aql_txq_limit_low,
local->airtime[IEEE80211_AC_BK].aql_txq_limit_high);
local->aql_txq_limit_low[IEEE80211_AC_VO],
local->aql_txq_limit_high[IEEE80211_AC_VO],
local->aql_txq_limit_low[IEEE80211_AC_VI],
local->aql_txq_limit_high[IEEE80211_AC_VI],
local->aql_txq_limit_low[IEEE80211_AC_BE],
local->aql_txq_limit_high[IEEE80211_AC_BE],
local->aql_txq_limit_low[IEEE80211_AC_BK],
local->aql_txq_limit_high[IEEE80211_AC_BK]);
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
......@@ -255,11 +255,11 @@ static ssize_t aql_txq_limit_write(struct file *file,
if (ac >= IEEE80211_NUM_ACS)
return -EINVAL;
q_limit_low_old = local->airtime[ac].aql_txq_limit_low;
q_limit_high_old = local->airtime[ac].aql_txq_limit_high;
q_limit_low_old = local->aql_txq_limit_low[ac];
q_limit_high_old = local->aql_txq_limit_high[ac];
local->airtime[ac].aql_txq_limit_low = q_limit_low;
local->airtime[ac].aql_txq_limit_high = q_limit_high;
local->aql_txq_limit_low[ac] = q_limit_low;
local->aql_txq_limit_high[ac] = q_limit_high;
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
......@@ -382,46 +382,6 @@ static const struct file_operations force_tx_status_ops = {
.llseek = default_llseek,
};
static ssize_t airtime_read(struct file *file,
char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[200];
u64 v_t[IEEE80211_NUM_ACS];
u64 wt[IEEE80211_NUM_ACS];
int len = 0, ac;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->airtime[ac].lock);
v_t[ac] = local->airtime[ac].v_t;
wt[ac] = local->airtime[ac].weight_sum;
spin_unlock_bh(&local->airtime[ac].lock);
}
len = scnprintf(buf, sizeof(buf),
"\tVO VI BE BK\n"
"Virt-t\t%-10llu %-10llu %-10llu %-10llu\n"
"Weight\t%-10llu %-10llu %-10llu %-10llu\n",
v_t[0],
v_t[1],
v_t[2],
v_t[3],
wt[0],
wt[1],
wt[2],
wt[3]);
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
static const struct file_operations airtime_ops = {
.read = airtime_read,
.open = simple_open,
.llseek = default_llseek,
};
#ifdef CONFIG_PM
static ssize_t reset_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
......@@ -675,11 +635,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
if (local->ops->wake_tx_queue)
DEBUGFS_ADD_MODE(aqm, 0600);
if (wiphy_ext_feature_isset(local->hw.wiphy,
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
DEBUGFS_ADD_MODE(airtime, 0600);
DEBUGFS_ADD_MODE(airtime_flags, 0600);
}
DEBUGFS_ADD_MODE(airtime_flags, 0600);
DEBUGFS_ADD(aql_txq_limit);
debugfs_create_u32("aql_threshold", 0600,
......
......@@ -510,34 +510,6 @@ static ssize_t ieee80211_if_fmt_aqm(
}
IEEE80211_IF_FILE_R(aqm);
static ssize_t ieee80211_if_fmt_airtime(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_txq *txq = sdata->vif.txq;
struct airtime_info *air_info;
int len;
if (!txq)
return 0;
spin_lock_bh(&local->airtime[txq->ac].lock);
air_info = to_airtime_info(txq);
len = scnprintf(buf,
buflen,
"RX: %llu us\nTX: %llu us\nWeight: %u\n"
"Virt-T: %lld us\n",
air_info->rx_airtime,
air_info->tx_airtime,
air_info->weight,
air_info->v_t);
spin_unlock_bh(&local->airtime[txq->ac].lock);
return len;
}
IEEE80211_IF_FILE_R(airtime);
IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX);
/* IBSS attributes */
......@@ -683,10 +655,8 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
if (sdata->local->ops->wake_tx_queue &&
sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
sdata->vif.type != NL80211_IFTYPE_NAN) {
sdata->vif.type != NL80211_IFTYPE_NAN)
DEBUGFS_ADD(aqm);
DEBUGFS_ADD(airtime);
}
}
static void add_sta_files(struct ieee80211_sub_if_data *sdata)
......
......@@ -202,7 +202,7 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
size_t bufsz = 400;
char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
u64 rx_airtime = 0, tx_airtime = 0;
u64 v_t[IEEE80211_NUM_ACS];
s64 deficit[IEEE80211_NUM_ACS];
ssize_t rv;
int ac;
......@@ -210,18 +210,18 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
return -ENOMEM;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->airtime[ac].lock);
spin_lock_bh(&local->active_txq_lock[ac]);
rx_airtime += sta->airtime[ac].rx_airtime;
tx_airtime += sta->airtime[ac].tx_airtime;
v_t[ac] = sta->airtime[ac].v_t;
spin_unlock_bh(&local->airtime[ac].lock);
deficit[ac] = sta->airtime[ac].deficit;
spin_unlock_bh(&local->active_txq_lock[ac]);
}
p += scnprintf(p, bufsz + buf - p,
"RX: %llu us\nTX: %llu us\nWeight: %u\n"
"Virt-T: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
rx_airtime, tx_airtime, sta->airtime[0].weight,
v_t[0], v_t[1], v_t[2], v_t[3]);
"Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
rx_airtime, tx_airtime, sta->airtime_weight,
deficit[0], deficit[1], deficit[2], deficit[3]);
rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
kfree(buf);
......@@ -236,11 +236,11 @@ static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
int ac;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->airtime[ac].lock);
spin_lock_bh(&local->active_txq_lock[ac]);
sta->airtime[ac].rx_airtime = 0;
sta->airtime[ac].tx_airtime = 0;
sta->airtime[ac].v_t = 0;
spin_unlock_bh(&local->airtime[ac].lock);
sta->airtime[ac].deficit = sta->airtime_weight;
spin_unlock_bh(&local->active_txq_lock[ac]);
}
return count;
......@@ -263,10 +263,10 @@ static ssize_t sta_aql_read(struct file *file, char __user *userbuf,
return -ENOMEM;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->airtime[ac].lock);
spin_lock_bh(&local->active_txq_lock[ac]);
q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
spin_unlock_bh(&local->airtime[ac].lock);
spin_unlock_bh(&local->active_txq_lock[ac]);
q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
}
......
......@@ -818,16 +818,20 @@ enum txq_info_flags {
* @def_flow: used as a fallback flow when a packet destined to @tin hashes to
* a fq_flow which is already owned by a different tin
* @def_cvars: codel vars for @def_flow
* @schedule_order: used with ieee80211_local->active_txqs
* @frags: used to keep fragments created after dequeue
* @schedule_order: used with ieee80211_local->active_txqs
* @schedule_round: counter to prevent infinite loops on TXQ scheduling
*/
struct txq_info {
struct fq_tin tin;
struct codel_vars def_cvars;
struct codel_stats cstats;
struct rb_node schedule_order;
u16 schedule_round;
struct list_head schedule_order;
struct sk_buff_head frags;
unsigned long flags;
/* keep last! */
......@@ -923,8 +927,6 @@ struct ieee80211_link_data {
struct ieee80211_key __rcu *default_mgmt_key;
struct ieee80211_key __rcu *default_beacon_key;
struct airtime_info airtime[IEEE80211_NUM_ACS];
struct work_struct csa_finalize_work;
bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
struct cfg80211_chan_def csa_chandef;
......@@ -1208,44 +1210,6 @@ enum mac80211_scan_state {
SCAN_ABORT,
};
/**
* struct airtime_sched_info - state used for airtime scheduling and AQL
*
* @lock: spinlock that protects all the fields in this struct
* @active_txqs: rbtree of currently backlogged queues, sorted by virtual time
* @schedule_pos: the current position maintained while a driver walks the tree
* with ieee80211_next_txq()
* @active_list: list of struct airtime_info structs that were active within
* the last AIRTIME_ACTIVE_DURATION (100 ms), used to compute
* weight_sum
* @last_weight_update: used for rate limiting walking active_list
* @last_schedule_time: tracks the last time a transmission was scheduled; used
* for catching up v_t if no stations are eligible for
* transmission.
* @v_t: global virtual time; queues with v_t < this are eligible for
* transmission
* @weight_sum: total sum of all active stations used for dividing airtime
* @weight_sum_reciprocal: reciprocal of weight_sum (to avoid divisions in fast
* path - see comment above
* IEEE80211_RECIPROCAL_DIVISOR_64)
* @aql_txq_limit_low: AQL limit when total outstanding airtime
* is < IEEE80211_AQL_THRESHOLD
* @aql_txq_limit_high: AQL limit when total outstanding airtime
* is > IEEE80211_AQL_THRESHOLD
*/
struct airtime_sched_info {
spinlock_t lock;
struct rb_root_cached active_txqs;
struct rb_node *schedule_pos;
struct list_head active_list;
u64 last_weight_update;
u64 last_schedule_activity;
u64 v_t;
u64 weight_sum;
u64 weight_sum_reciprocal;
u32 aql_txq_limit_low;
u32 aql_txq_limit_high;
};
DECLARE_STATIC_KEY_FALSE(aql_disable);
struct ieee80211_local {
......@@ -1259,8 +1223,13 @@ struct ieee80211_local {
struct codel_params cparams;
/* protects active_txqs and txqi->schedule_order */
struct airtime_sched_info airtime[IEEE80211_NUM_ACS];
spinlock_t active_txq_lock[IEEE80211_NUM_ACS];
struct list_head active_txqs[IEEE80211_NUM_ACS];
u16 schedule_round[IEEE80211_NUM_ACS];
u16 airtime_flags;
u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
u32 aql_threshold;
atomic_t aql_total_pending_airtime;
......@@ -1683,125 +1652,6 @@ static inline bool txq_has_queue(struct ieee80211_txq *txq)
return !(skb_queue_empty(&txqi->frags) && !txqi->tin.backlog_packets);
}
static inline struct airtime_info *to_airtime_info(struct ieee80211_txq *txq)
{
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
if (txq->sta) {
sta = container_of(txq->sta, struct sta_info, sta);
return &sta->airtime[txq->ac];
}
sdata = vif_to_sdata(txq->vif);
return &sdata->deflink.airtime[txq->ac];
}
/* To avoid divisions in the fast path, we keep pre-computed reciprocals for
* airtime weight calculations. There are two different weights to keep track
* of: The per-station weight and the sum of weights per phy.
*
* For the per-station weights (kept in airtime_info below), we use 32-bit
* reciprocals with a devisor of 2^19. This lets us keep the multiplications and
* divisions for the station weights as 32-bit operations at the cost of a bit
* of rounding error for high weights; but the choice of divisor keeps rounding
* errors <10% for weights <2^15, assuming no more than 8ms of airtime is
* reported at a time.
*
* For the per-phy sum of weights the values can get higher, so we use 64-bit
* operations for those with a 32-bit divisor, which should avoid any
* significant rounding errors.
*/
#define IEEE80211_RECIPROCAL_DIVISOR_64 0x100000000ULL
#define IEEE80211_RECIPROCAL_SHIFT_64 32
#define IEEE80211_RECIPROCAL_DIVISOR_32 0x80000U
#define IEEE80211_RECIPROCAL_SHIFT_32 19
static inline void airtime_weight_set(struct airtime_info *air_info, u16 weight)
{
if (air_info->weight == weight)
return;
air_info->weight = weight;
if (weight) {
air_info->weight_reciprocal =
IEEE80211_RECIPROCAL_DIVISOR_32 / weight;
} else {
air_info->weight_reciprocal = 0;
}
}
static inline void airtime_weight_sum_set(struct airtime_sched_info *air_sched,
int weight_sum)
{
if (air_sched->weight_sum == weight_sum)
return;
air_sched->weight_sum = weight_sum;
if (air_sched->weight_sum) {
air_sched->weight_sum_reciprocal = IEEE80211_RECIPROCAL_DIVISOR_64;
do_div(air_sched->weight_sum_reciprocal, air_sched->weight_sum);
} else {
air_sched->weight_sum_reciprocal = 0;
}
}
/* A problem when trying to enforce airtime fairness is that we want to divide
* the airtime between the currently *active* stations. However, basing this on
* the instantaneous queue state of stations doesn't work, as queues tend to
* oscillate very quickly between empty and occupied, leading to the scheduler
* thinking only a single station is active when deciding whether to allow
* transmission (and thus not throttling correctly).
*
* To fix this we use a timer-based notion of activity: a station is considered
* active if it has been scheduled within the last 100 ms; we keep a separate
* list of all the stations considered active in this manner, and lazily update
* the total weight of active stations from this list (filtering the stations in
* the list by their 'last active' time).
*
* We add one additional safeguard to guard against stations that manage to get
* scheduled every 100 ms but don't transmit a lot of data, and thus don't use
* up any airtime. Such stations would be able to get priority for an extended
* period of time if they do start transmitting at full capacity again, and so
* we add an explicit maximum for how far behind a station is allowed to fall in
* the virtual airtime domain. This limit is set to a relatively high value of
* 20 ms because the main mechanism for catching up idle stations is the active
* state as described above; i.e., the hard limit should only be hit in
* pathological cases.
*/
#define AIRTIME_ACTIVE_DURATION (100 * NSEC_PER_MSEC)
#define AIRTIME_MAX_BEHIND 20000 /* 20 ms */
static inline bool airtime_is_active(struct airtime_info *air_info, u64 now)
{
return air_info->last_scheduled >= now - AIRTIME_ACTIVE_DURATION;
}
static inline void airtime_set_active(struct airtime_sched_info *air_sched,
struct airtime_info *air_info, u64 now)
{
air_info->last_scheduled = now;
air_sched->last_schedule_activity = now;
list_move_tail(&air_info->list, &air_sched->active_list);
}
static inline bool airtime_catchup_v_t(struct airtime_sched_info *air_sched,
u64 v_t, u64 now)
{
air_sched->v_t = v_t;
return true;
}
static inline void init_airtime_info(struct airtime_info *air_info,
struct airtime_sched_info *air_sched)
{
atomic_set(&air_info->aql_tx_pending, 0);
air_info->aql_limit_low = air_sched->aql_txq_limit_low;
air_info->aql_limit_high = air_sched->aql_txq_limit_high;
airtime_weight_set(air_info, IEEE80211_DEFAULT_AIRTIME_WEIGHT);
INIT_LIST_HEAD(&air_info->list);
}
static inline bool
ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status)
{
......@@ -2047,14 +1897,6 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
u64 *cookie);
int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len);
void ieee80211_resort_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq,
bool purge);
void ieee80211_update_airtime_weight(struct ieee80211_local *local,
struct airtime_sched_info *air_sched,
u64 now, bool force);
/* HT */
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
......
......@@ -2176,10 +2176,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
}
}
for (i = 0; i < IEEE80211_NUM_ACS; i++)
init_airtime_info(&sdata->deflink.airtime[i],
&local->airtime[i]);
ieee80211_set_default_queues(sdata);
sdata->deflink.ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
......
......@@ -784,13 +784,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
spin_lock_init(&local->queue_stop_reason_lock);
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
struct airtime_sched_info *air_sched = &local->airtime[i];
air_sched->active_txqs = RB_ROOT_CACHED;
INIT_LIST_HEAD(&air_sched->active_list);
spin_lock_init(&air_sched->lock);
air_sched->aql_txq_limit_low = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
air_sched->aql_txq_limit_high =
INIT_LIST_HEAD(&local->active_txqs[i]);
spin_lock_init(&local->active_txq_lock[i]);
local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
local->aql_txq_limit_high[i] =
IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
}
......
......@@ -1565,8 +1565,12 @@ static void sta_ps_start(struct sta_info *sta)
for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
struct ieee80211_txq *txq = sta->sta.txq[tid];
struct txq_info *txqi = to_txq_info(txq);
ieee80211_unschedule_txq(&local->hw, txq, false);
spin_lock(&local->active_txq_lock[txq->ac]);
if (!list_empty(&txqi->schedule_order))
list_del_init(&txqi->schedule_order);
spin_unlock(&local->active_txq_lock[txq->ac]);
if (txq_has_queue(txq))
set_bit(tid, &sta->txq_buffered_tids);
......
......@@ -559,11 +559,15 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
if (sta_prepare_rate_control(local, sta, gfp))
goto free_txq;
sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
skb_queue_head_init(&sta->ps_tx_buf[i]);
skb_queue_head_init(&sta->tx_filtered[i]);
init_airtime_info(&sta->airtime[i], &local->airtime[i]);
sta->airtime[i].deficit = sta->airtime_weight;
atomic_set(&sta->airtime[i].aql_tx_pending, 0);
sta->airtime[i].aql_limit_low = local->aql_txq_limit_low[i];
sta->airtime[i].aql_limit_high = local->aql_txq_limit_high[i];
}
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
......@@ -2035,59 +2039,24 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
}
EXPORT_SYMBOL(ieee80211_sta_set_buffered);
void ieee80211_register_airtime(struct ieee80211_txq *txq,
u32 tx_airtime, u32 rx_airtime)
void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
u32 tx_airtime, u32 rx_airtime)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
struct ieee80211_local *local = sdata->local;
u64 weight_sum, weight_sum_reciprocal;
struct airtime_sched_info *air_sched;
struct airtime_info *air_info;
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
struct ieee80211_local *local = sta->sdata->local;
u8 ac = ieee80211_ac_from_tid(tid);
u32 airtime = 0;
air_sched = &local->airtime[txq->ac];
air_info = to_airtime_info(txq);
if (local->airtime_flags & AIRTIME_USE_TX)
if (sta->local->airtime_flags & AIRTIME_USE_TX)
airtime += tx_airtime;
if (local->airtime_flags & AIRTIME_USE_RX)
if (sta->local->airtime_flags & AIRTIME_USE_RX)
airtime += rx_airtime;
/* Weights scale so the unit weight is 256 */
airtime <<= 8;
spin_lock_bh(&air_sched->lock);
air_info->tx_airtime += tx_airtime;
air_info->rx_airtime += rx_airtime;
if (air_sched->weight_sum) {
weight_sum = air_sched->weight_sum;
weight_sum_reciprocal = air_sched->weight_sum_reciprocal;
} else {
weight_sum = air_info->weight;
weight_sum_reciprocal = air_info->weight_reciprocal;
}
/* Round the calculation of global vt */
air_sched->v_t += (u64)((airtime + (weight_sum >> 1)) *
weight_sum_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_64;
air_info->v_t += (u32)((airtime + (air_info->weight >> 1)) *
air_info->weight_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_32;
ieee80211_resort_txq(&local->hw, txq);
spin_unlock_bh(&air_sched->lock);
}
void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
u32 tx_airtime, u32 rx_airtime)
{
struct ieee80211_txq *txq = pubsta->txq[tid];
if (!txq)
return;
ieee80211_register_airtime(txq, tx_airtime, rx_airtime);
spin_lock_bh(&local->active_txq_lock[ac]);
sta->airtime[ac].tx_airtime += tx_airtime;
sta->airtime[ac].rx_airtime += rx_airtime;
sta->airtime[ac].deficit -= airtime;
spin_unlock_bh(&local->active_txq_lock[ac]);
}
EXPORT_SYMBOL(ieee80211_sta_register_airtime);
......@@ -2501,7 +2470,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
}
if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
sinfo->airtime_weight = sta->airtime[0].weight;
sinfo->airtime_weight = sta->airtime_weight;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
}
......
......@@ -135,25 +135,18 @@ enum ieee80211_agg_stop_reason {
#define AIRTIME_USE_TX BIT(0)
#define AIRTIME_USE_RX BIT(1)
struct airtime_info {
u64 rx_airtime;
u64 tx_airtime;
u64 v_t;
u64 last_scheduled;
struct list_head list;
s64 deficit;
atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
u32 aql_limit_low;
u32 aql_limit_high;
u32 weight_reciprocal;
u16 weight;
};
void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
struct sta_info *sta, u8 ac,
u16 tx_airtime, bool tx_completed);
void ieee80211_register_airtime(struct ieee80211_txq *txq,
u32 tx_airtime, u32 rx_airtime);
struct sta_info;
......@@ -609,6 +602,7 @@ struct link_sta_info {
* @tid_seq: per-TID sequence numbers for sending to this STA
* @airtime: per-AC struct airtime_info describing airtime statistics for this
* station
* @airtime_weight: station weight for airtime fairness calculation purposes
* @ampdu_mlme: A-MPDU state machine state
* @mesh: mesh STA information
* @debugfs_dir: debug filesystem directory dentry
......@@ -693,6 +687,7 @@ struct sta_info {
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
struct airtime_info airtime[IEEE80211_NUM_ACS];
u16 airtime_weight;
/*
* Aggregation information, locked with lock.
......
......@@ -998,25 +998,6 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)
ieee80211_frame_acked(sta, skb);
} else if (wiphy_ext_feature_isset(local->hw.wiphy,
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_txq *txq;
u32 airtime;
/* Account airtime to multicast queue */
sdata = ieee80211_sdata_from_skb(local, skb);
if (sdata && (txq = sdata->vif.txq)) {
airtime = info->status.tx_time ?:
ieee80211_calc_expected_tx_airtime(hw,
&sdata->vif,
NULL,
skb->len,
false);
ieee80211_register_airtime(txq, airtime, 0);
}
}
/* SNMP counters
......
This diff is collapsed.
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