]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
mac80211: Add airtime account and scheduling to TXQs
authorToke Høiland-Jørgensen <toke@toke.dk>
Tue, 31 Oct 2017 11:27:46 +0000 (12:27 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 11 Dec 2017 11:40:24 +0000 (12:40 +0100)
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>
include/net/mac80211.h
net/mac80211/debugfs.c
net/mac80211/debugfs_sta.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tx.c

index 45155803c875980e0b3364f621b330e558197bb3..531b526a10dbc82572e7a87e08395305e490c871 100644 (file)
@@ -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
index 1f466d12a6bcd62061f4bab78d400544da93dd5c..d6b87a4ec3e96b7f948843bfecf7552bf2263ef6 100644 (file)
@@ -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
 };
 
index b15412c21ac9355762a0b99ad99096d65a0a001b..40dba446836f1030530f1c6088b62f2dfe9b47f6 100644 (file)
@@ -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,
index 4155838c7beffbd6e8df4c3d837186cc2a3b1031..120c516851cf679b72948acecce0273d28b47ab8 100644 (file)
@@ -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;
 
index 935d6e2491b18a6d65891aba88668fb622e6973b..b7142f8491d08109bf6286387d0f503d54a75417 100644 (file)
@@ -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);
index b3cff69bfd660ae8c2a1a0c9cc4ae089d0d052a5..808f41fb536a11e9593a475a68aab5a55e8e912b 100644 (file)
@@ -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.
index e0bcf16df4949b9bac235a6aecbed240b8906d29..ed5500e8aafb6bf89aa907d072d79fbd926b3377 100644 (file)
@@ -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;
index cd53619435b641c446ed4e0e69eda1d97714f54a..e356f2f85e124aa8ffb66d51f8fd5f0661215f3c 100644 (file)
@@ -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.
         */
index da7427a415299e5c18c4c7c00255e80297aab032..b044dbed2bb12b420efb6dfdd1da7b970d38a65a 100644 (file)
@@ -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;
 
index 842881ca8f20e82b31f5b3dfd951a85c9a6a31ee..18381581b5e93231746a6d2571f59c38fa294876 100644 (file)
@@ -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: