mac80211: Add airtime account and scheduling to TXQs
This adds airtime accounting and scheduling to the mac80211 TXQ scheduler. A new hardware flag, AIRTIME_ACCOUNTING, is added that drivers can set if they support reporting airtime usage of transmissions. When this flag is set, mac80211 will expect the actual airtime usage to be reported in the tx_time and rx_time fields of the respective status structs. When airtime information is present, mac80211 will schedule TXQs (through ieee80211_next_txq()) in a way that enforces airtime fairness between active stations. This scheduling works the same way as the ath9k in-driver airtime fairness scheduling. Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:

committed by
Johannes Berg

parent
e937b8da5a
commit
b0d52ad821
@@ -212,6 +212,7 @@ static const char *hw_flag_names[] = {
|
||||
FLAG(REPORTS_LOW_ACK),
|
||||
FLAG(SUPPORTS_TX_FRAG),
|
||||
FLAG(SUPPORTS_TDLS_BUFFER_STA),
|
||||
FLAG(AIRTIME_ACCOUNTING),
|
||||
#undef FLAG
|
||||
};
|
||||
|
||||
|
@@ -188,6 +188,32 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
|
||||
}
|
||||
STA_OPS(aqm);
|
||||
|
||||
static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct sta_info *sta = file->private_data;
|
||||
size_t bufsz = 200;
|
||||
char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
|
||||
ssize_t rv;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
|
||||
p += scnprintf(p, bufsz + buf - p,
|
||||
"RX: %llu us\nTX: %llu us\nDeficit: %lld us\n",
|
||||
sta->airtime_stats.rx_airtime,
|
||||
sta->airtime_stats.tx_airtime,
|
||||
sta->airtime_deficit);
|
||||
|
||||
spin_unlock_bh(&sta->lock);
|
||||
rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
|
||||
kfree(buf);
|
||||
return rv;
|
||||
}
|
||||
STA_OPS(airtime);
|
||||
|
||||
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@@ -542,6 +568,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
||||
if (local->ops->wake_tx_queue)
|
||||
DEBUGFS_ADD(aqm);
|
||||
|
||||
if (ieee80211_hw_check(&local->hw, AIRTIME_ACCOUNTING))
|
||||
DEBUGFS_ADD(airtime);
|
||||
|
||||
if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
|
||||
debugfs_create_x32("driver_buffered_tids", 0400,
|
||||
sta->debugfs_dir,
|
||||
|
@@ -90,6 +90,9 @@ extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS];
|
||||
|
||||
#define IEEE80211_MAX_NAN_INSTANCE_ID 255
|
||||
|
||||
/* How much to increase airtime deficit on each scheduling round */
|
||||
#define IEEE80211_AIRTIME_QUANTUM 1000 /* usec */
|
||||
|
||||
struct ieee80211_fragment_entry {
|
||||
struct sk_buff_head skb_list;
|
||||
unsigned long first_frag_time;
|
||||
@@ -1123,9 +1126,10 @@ struct ieee80211_local {
|
||||
struct codel_vars *cvars;
|
||||
struct codel_params cparams;
|
||||
|
||||
/* protects active_txqs and txqi->schedule_order */
|
||||
/* protects active_txqs_{new,old} and txqi->schedule_order */
|
||||
spinlock_t active_txq_lock;
|
||||
struct list_head active_txqs;
|
||||
struct list_head active_txqs_new;
|
||||
struct list_head active_txqs_old;
|
||||
|
||||
const struct ieee80211_ops *ops;
|
||||
|
||||
|
@@ -619,7 +619,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
||||
spin_lock_init(&local->rx_path_lock);
|
||||
spin_lock_init(&local->queue_stop_reason_lock);
|
||||
|
||||
INIT_LIST_HEAD(&local->active_txqs);
|
||||
INIT_LIST_HEAD(&local->active_txqs_new);
|
||||
INIT_LIST_HEAD(&local->active_txqs_old);
|
||||
spin_lock_init(&local->active_txq_lock);
|
||||
|
||||
INIT_LIST_HEAD(&local->chanctx_list);
|
||||
|
@@ -1630,6 +1630,14 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
||||
if (ieee80211_vif_is_mesh(&rx->sdata->vif))
|
||||
ieee80211_mps_rx_h_sta_process(sta, hdr);
|
||||
|
||||
/* airtime accounting */
|
||||
if (status->airtime) {
|
||||
spin_lock_bh(&sta->lock);
|
||||
sta->airtime_stats.rx_airtime += status->airtime;
|
||||
sta->airtime_deficit -= status->airtime;
|
||||
spin_unlock_bh(&sta->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop (qos-)data::nullfunc frames silently, since they
|
||||
* are used only to control station power saving mode.
|
||||
|
@@ -425,6 +425,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
||||
sta->cparams.interval = MS2TIME(100);
|
||||
sta->cparams.ecn = true;
|
||||
|
||||
sta->airtime_deficit = IEEE80211_AIRTIME_QUANTUM;
|
||||
|
||||
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
|
||||
|
||||
return sta;
|
||||
|
@@ -559,6 +559,13 @@ struct sta_info {
|
||||
} tx_stats;
|
||||
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
|
||||
|
||||
/* Airtime stats and deficit, protected by lock */
|
||||
struct {
|
||||
u64 rx_airtime;
|
||||
u64 tx_airtime;
|
||||
} airtime_stats;
|
||||
s64 airtime_deficit;
|
||||
|
||||
/*
|
||||
* Aggregation information, locked with lock.
|
||||
*/
|
||||
|
@@ -823,6 +823,14 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
|
||||
ieee80211_lost_packet(sta, info);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->status.tx_time &&
|
||||
ieee80211_hw_check(&local->hw, AIRTIME_ACCOUNTING)) {
|
||||
spin_lock_bh(&sta->lock);
|
||||
sta->airtime_stats.tx_airtime += info->status.tx_time;
|
||||
sta->airtime_deficit -= info->status.tx_time;
|
||||
spin_unlock_bh(&sta->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* SNMP counters
|
||||
@@ -947,6 +955,14 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
|
||||
sta->status_stats.retry_failed++;
|
||||
sta->status_stats.retry_count += retry_count;
|
||||
|
||||
if (info->status.tx_time &&
|
||||
ieee80211_hw_check(&local->hw, AIRTIME_ACCOUNTING)) {
|
||||
spin_lock_bh(&sta->lock);
|
||||
sta->airtime_stats.tx_airtime += info->status.tx_time;
|
||||
sta->airtime_deficit -= info->status.tx_time;
|
||||
spin_unlock_bh(&sta->lock);
|
||||
}
|
||||
|
||||
if (acked) {
|
||||
sta->status_stats.last_ack = jiffies;
|
||||
|
||||
|
@@ -3566,7 +3566,7 @@ bool ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
||||
spin_lock_bh(&local->active_txq_lock);
|
||||
|
||||
if (list_empty(&txqi->schedule_order)) {
|
||||
list_add_tail(&txqi->schedule_order, &local->active_txqs);
|
||||
list_add_tail(&txqi->schedule_order, &local->active_txqs_new);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
@@ -3580,14 +3580,35 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct txq_info *txqi = NULL;
|
||||
struct list_head *head;
|
||||
|
||||
spin_lock_bh(&local->active_txq_lock);
|
||||
|
||||
if (list_empty(&local->active_txqs))
|
||||
goto out;
|
||||
begin:
|
||||
head = &local->active_txqs_new;
|
||||
if (list_empty(head)) {
|
||||
head = &local->active_txqs_old;
|
||||
if (list_empty(head))
|
||||
goto out;
|
||||
}
|
||||
|
||||
txqi = list_first_entry(head, struct txq_info, schedule_order);
|
||||
|
||||
if (txqi->txq.sta) {
|
||||
struct sta_info *sta = container_of(txqi->txq.sta,
|
||||
struct sta_info, sta);
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
if (sta->airtime_deficit < 0) {
|
||||
sta->airtime_deficit += IEEE80211_AIRTIME_QUANTUM;
|
||||
list_move_tail(&txqi->schedule_order,
|
||||
&local->active_txqs_old);
|
||||
spin_unlock_bh(&sta->lock);
|
||||
goto begin;
|
||||
}
|
||||
spin_unlock_bh(&sta->lock);
|
||||
}
|
||||
|
||||
txqi = list_first_entry(&local->active_txqs,
|
||||
struct txq_info, schedule_order);
|
||||
list_del_init(&txqi->schedule_order);
|
||||
|
||||
out:
|
||||
|
Reference in New Issue
Block a user