summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/mac80211.h24
-rw-r--r--net/mac80211/debugfs.c1
-rw-r--r--net/mac80211/debugfs_sta.c29
-rw-r--r--net/mac80211/ieee80211_i.h8
-rw-r--r--net/mac80211/main.c3
-rw-r--r--net/mac80211/rx.c8
-rw-r--r--net/mac80211/sta_info.c2
-rw-r--r--net/mac80211/sta_info.h7
-rw-r--r--net/mac80211/status.c16
-rw-r--r--net/mac80211/tx.c31
10 files changed, 121 insertions, 8 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 45155803c875..531b526a10db 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1188,6 +1188,8 @@ enum mac80211_rx_encoding {
* HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
* @nss: number of streams (VHT and HE only)
* @flag: %RX_FLAG_\*
+ * @airtime: Duration of frame in usec. See @IEEE80211_HW_AIRTIME_ACCOUNTING for
+ * how to use this.
* @encoding: &enum mac80211_rx_encoding
* @bw: &enum rate_info_bw
* @enc_flags: uses bits from &enum mac80211_rx_encoding_flags
@@ -1202,6 +1204,7 @@ struct ieee80211_rx_status {
u32 device_timestamp;
u32 ampdu_reference;
u32 flag;
+ u16 airtime;
u16 freq;
u8 enc_flags;
u8 encoding:2, bw:3;
@@ -2066,6 +2069,26 @@ struct ieee80211_txq {
* @IEEE80211_HW_SUPPORTS_TDLS_BUFFER_STA: Hardware supports buffer STA on
* TDLS links.
*
+ * @IEEE80211_HW_AIRTIME_ACCOUNTING: Hardware supports accounting the airtime
+ * usage of other stations and reports it in the @tx_time and/or @airtime
+ * fields of the TX/RX status structs.
+ * When setting this flag, the driver should ensure that the respective
+ * fields in the TX and RX status structs are always either zero or
+ * contains a valid duration for the frame in usec. The driver can choose
+ * to report either or both of TX and RX airtime, but it is recommended to
+ * report both.
+ * The reported airtime should as a minimum include all time that is spent
+ * transmitting to the remote station, including overhead and padding, but
+ * not including time spent waiting for a TXOP. If the time is not reported
+ * by the hardware it can in some cases be calculated from the rate and
+ * known frame composition. When possible, the time should include any
+ * failed transmission attempts.
+ * For aggregated frames, there are two possible strategies to report the
+ * airtime: Either include the airtime of the entire aggregate in the first
+ * (or last) frame and leave the others at zero. Alternatively, include the
+ * overhead of the full aggregate in the first or last frame and report the
+ * time of each frame + padding not including the full aggregate overhead.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -2109,6 +2132,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_REPORTS_LOW_ACK,
IEEE80211_HW_SUPPORTS_TX_FRAG,
IEEE80211_HW_SUPPORTS_TDLS_BUFFER_STA,
+ IEEE80211_HW_AIRTIME_ACCOUNTING,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 1f466d12a6bc..d6b87a4ec3e9 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -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
};
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index b15412c21ac9..40dba446836f 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -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,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4155838c7bef..120c516851cf 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -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;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 935d6e2491b1..b7142f8491d0 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -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);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index b3cff69bfd66..808f41fb536a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -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.
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index e0bcf16df494..ed5500e8aafb 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -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;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index cd53619435b6..e356f2f85e12 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -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.
*/
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index da7427a41529..b044dbed2bb1 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -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;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 842881ca8f20..18381581b5e9 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -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: