diff options
-rw-r--r-- | include/net/mac80211.h | 24 | ||||
-rw-r--r-- | net/mac80211/debugfs.c | 1 | ||||
-rw-r--r-- | net/mac80211/debugfs_sta.c | 29 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 8 | ||||
-rw-r--r-- | net/mac80211/main.c | 3 | ||||
-rw-r--r-- | net/mac80211/rx.c | 8 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 2 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 7 | ||||
-rw-r--r-- | net/mac80211/status.c | 16 | ||||
-rw-r--r-- | net/mac80211/tx.c | 31 |
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: |