Skip to content

Commit

Permalink
mac80211: Switch to a virtual time-based airtime scheduler
Browse files Browse the repository at this point in the history
This switches the airtime scheduler in mac80211 to use a virtual
time-based scheduler instead of the round-robin scheduler used before.
This has a couple of advantages:

- No need to sync up the round-robin scheduler in firmware/hardware with
  the round-robin airtime scheduler.

- If several stations are eligible for transmission we can schedule both
  of them; no need to hard-block the scheduling rotation until the head
  of the queue has used up its quantum.

- The check of whether a station is eligible for transmission becomes
  simpler (in ieee80211_txq_may_transmit()).

The drawback is that scheduling becomes slightly more expensive, as we
need to maintain an rbtree of TXQs sorted by virtual time. This means
that ieee80211_register_airtime() becomes O(logN) in the number of
currently scheduled TXQs because it can change the order of the
scheduled stations. We mitigate this overhead by only resorting when a
station changes position in the tree, and hopefully N rarely grows too
big (it's only TXQs currently backlogged, not all associated stations),
so it shouldn't be too big of an issue.

To prevent divisions in the fast path, we maintain both station sums and
pre-computed reciprocals of the sums. This turns the fast-path operation
into a multiplication, with divisions only happening as the number of
active stations change (to re-compute the current sum of all active
station weights). To prevent this re-computation of the reciprocal from
happening too frequently, we use a time-based notion of station
activity, instead of updating the weight every time a station gets
scheduled or de-scheduled. As queues can oscillate between empty and
occupied quite frequently, this can significantly cut down on the number
of re-computations. It also has the added benefit of making the station
airtime calculation independent on whether the queue happened to have
drained at the time an airtime value was accounted.

Co-developed-by: Yibo Zhao <yiboz@codeaurora.org>
Signed-off-by: Yibo Zhao <yiboz@codeaurora.org>
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Link: https://lore.kernel.org/r/20210623134755.235545-1-toke@redhat.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Toke Høiland-Jørgensen authored and Johannes Berg committed Jun 23, 2021
1 parent 2832943 commit 2433647
Show file tree
Hide file tree
Showing 13 changed files with 653 additions and 190 deletions.
17 changes: 3 additions & 14 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -6605,9 +6605,6 @@ 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
*
Expand All @@ -6620,11 +6617,7 @@ void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
* The driver may call this function if it has buffered packets for
* this TXQ internally.
*/
static inline void
ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
__ieee80211_schedule_txq(hw, txq, true);
}
void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);

/**
* ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
Expand All @@ -6636,12 +6629,8 @@ 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.
*/
static inline void
ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool force)
{
__ieee80211_schedule_txq(hw, txq, force);
}
void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool force);

/**
* ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
Expand Down
35 changes: 34 additions & 1 deletion net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,38 @@ 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)
Expand Down Expand Up @@ -1629,7 +1661,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta_apply_mesh_params(local, sta, params);

if (params->airtime_weight)
sta->airtime_weight = params->airtime_weight;
sta_apply_airtime_params(local, sta, params);


/* set the STA state after all sta info from usermode has been set */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
Expand Down
70 changes: 57 additions & 13 deletions net/mac80211/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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->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]);
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);
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
Expand Down Expand Up @@ -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->aql_txq_limit_low[ac];
q_limit_high_old = local->aql_txq_limit_high[ac];
q_limit_low_old = local->airtime[ac].aql_txq_limit_low;
q_limit_high_old = local->airtime[ac].aql_txq_limit_high;

local->aql_txq_limit_low[ac] = q_limit_low;
local->aql_txq_limit_high[ac] = q_limit_high;
local->airtime[ac].aql_txq_limit_low = q_limit_low;
local->airtime[ac].aql_txq_limit_high = q_limit_high;

mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
Expand Down Expand Up @@ -382,6 +382,46 @@ 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)
Expand Down Expand Up @@ -632,7 +672,11 @@ void debugfs_hw_add(struct ieee80211_local *local)
if (local->ops->wake_tx_queue)
DEBUGFS_ADD_MODE(aqm, 0600);

DEBUGFS_ADD_MODE(airtime_flags, 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(aql_txq_limit);
debugfs_create_u32("aql_threshold", 0600,
Expand Down
32 changes: 31 additions & 1 deletion net/mac80211/debugfs_netdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,34 @@ 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 */
Expand Down Expand Up @@ -657,8 +685,10 @@ 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)
Expand Down
24 changes: 12 additions & 12 deletions net/mac80211/debugfs_sta.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,26 +202,26 @@ 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;
s64 deficit[IEEE80211_NUM_ACS];
u64 v_t[IEEE80211_NUM_ACS];
ssize_t rv;
int ac;

if (!buf)
return -ENOMEM;

for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->active_txq_lock[ac]);
spin_lock_bh(&local->airtime[ac].lock);
rx_airtime += sta->airtime[ac].rx_airtime;
tx_airtime += sta->airtime[ac].tx_airtime;
deficit[ac] = sta->airtime[ac].deficit;
spin_unlock_bh(&local->active_txq_lock[ac]);
v_t[ac] = sta->airtime[ac].v_t;
spin_unlock_bh(&local->airtime[ac].lock);
}

p += scnprintf(p, bufsz + buf - p,
"RX: %llu us\nTX: %llu us\nWeight: %u\n"
"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]);
"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]);

rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
kfree(buf);
Expand All @@ -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->active_txq_lock[ac]);
spin_lock_bh(&local->airtime[ac].lock);
sta->airtime[ac].rx_airtime = 0;
sta->airtime[ac].tx_airtime = 0;
sta->airtime[ac].deficit = sta->airtime_weight;
spin_unlock_bh(&local->active_txq_lock[ac]);
sta->airtime[ac].v_t = 0;
spin_unlock_bh(&local->airtime[ac].lock);
}

return count;
Expand All @@ -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->active_txq_lock[ac]);
spin_lock_bh(&local->airtime[ac].lock);
q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
spin_unlock_bh(&local->active_txq_lock[ac]);
spin_unlock_bh(&local->airtime[ac].lock);
q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
}

Expand Down
Loading

0 comments on commit 2433647

Please sign in to comment.