From: Toke Høiland-Jørgensen <[email protected]>
The previous commit added the ability to throttle stations when they queue
too much airtime in the hardware. This commit enables the functionality by
calculating the expected airtime usage of each packet that is dequeued from
the TXQs in mac80211, and accounting that as pending airtime.
The estimated airtime for each skb is stored in the tx_info, so we can
subtract the same amount from the running total when the skb is freed or
recycled. The throttling mechanism relies on this accounting to be
accurate (i.e., that we are not freeing skbs without subtracting any
airtime they were accounted for), so we put the subtraction into
ieee80211_report_used_skb(). As an optimisation, we also subtract the
airtime on regular TX completion, zeroing out the value stored in the
packet afterwards, to avoid having to do an expensive lookup of the station
from the packet data on every packet.
This patch does *not* include any mechanism to wake a throttled TXQ again,
on the assumption that this will happen anyway as a side effect of whatever
freed the skb (most commonly a TX completion).
Signed-off-by: Toke Høiland-Jørgensen <[email protected]>
---
net/mac80211/status.c | 33 +++++++++++++++++++++++++++++++++
net/mac80211/tx.c | 21 +++++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index ab8ba5835ca0..ae15c8fd2421 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -676,6 +676,28 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
if (dropped)
acked = false;
+ if (info->tx_time_est) {
+ struct sta_info *sta = NULL, *s;
+ struct rhlist_head *tmp;
+
+ rcu_read_lock();
+
+ for_each_sta_info(local, hdr->addr1, s, tmp) {
+ /* skip wrong virtual interface */
+ if (!ether_addr_equal(hdr->addr2, s->sdata->vif.addr))
+ continue;
+
+ sta = s;
+ break;
+ }
+
+ ieee80211_sta_update_pending_airtime(local, sta,
+ skb_get_queue_mapping(skb),
+ info->tx_time_est << 2,
+ true);
+ rcu_read_unlock();
+ }
+
if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
struct ieee80211_sub_if_data *sdata;
@@ -986,6 +1008,17 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
ieee80211_sta_register_airtime(&sta->sta, tid,
info->status.tx_time, 0);
+ if (info->tx_time_est) {
+ /* Do this here to avoid the expensive lookup of the sta
+ * in ieee80211_report_used_skb().
+ */
+ ieee80211_sta_update_pending_airtime(local, sta,
+ skb_get_queue_mapping(skb),
+ info->tx_time_est << 2,
+ true);
+ info->tx_time_est = 0;
+ }
+
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
if (info->flags & IEEE80211_TX_STAT_ACK) {
if (sta->status_stats.lost_packets)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index f53d56ef535a..583211ca670d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -3549,6 +3549,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
WARN_ON_ONCE(softirq_count() == 0);
+ if (!ieee80211_txq_airtime_check(hw, txq))
+ return NULL;
+
begin:
spin_lock_bh(&fq->lock);
@@ -3659,6 +3662,24 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
}
IEEE80211_SKB_CB(skb)->control.vif = vif;
+
+ if (local->airtime_flags & AIRTIME_USE_AQL) {
+ u32 airtime;
+
+ airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
+ skb->len);
+ if (airtime) {
+ /* We only have 10 bits in tx_time_est, so store airtime
+ * in increments of 4us and clamp the maximum to 2**12-1
+ */
+ airtime = min_t(u32, airtime, 4095) & ~3U;
+ info->tx_time_est = airtime >> 2;
+ ieee80211_sta_update_pending_airtime(local, tx.sta,
+ txq->ac, airtime,
+ false);
+ }
+ }
+
return skb;
out:
--
2.24.0.rc1.363.gb1bccd3e3d-goog
Hi Kan,
I love your patch! Yet something to improve:
[auto build test ERROR on mac80211-next/master]
[cannot apply to v5.4-rc7 next-20191112]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Kan-Yan/Implement-Airtime-based-Queue-Limit-AQL/20191113-055705
base: https://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git master
config: sparc64-allmodconfig (attached as .config)
compiler: sparc64-linux-gcc (GCC) 7.4.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.4.0 make.cross ARCH=sparc64
If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <[email protected]>
All errors (new ones prefixed by >>):
net/mac80211/tx.c: In function 'ieee80211_tx_dequeue':
>> net/mac80211/tx.c:3671:13: error: implicit declaration of function 'ieee80211_calc_expected_tx_airtime'; did you mean 'ieee80211_sta_register_airtime'? [-Werror=implicit-function-declaration]
airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ieee80211_sta_register_airtime
cc1: some warnings being treated as errors
vim +3671 net/mac80211/tx.c
3537
3538 struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
3539 struct ieee80211_txq *txq)
3540 {
3541 struct ieee80211_local *local = hw_to_local(hw);
3542 struct txq_info *txqi = container_of(txq, struct txq_info, txq);
3543 struct ieee80211_hdr *hdr;
3544 struct sk_buff *skb = NULL;
3545 struct fq *fq = &local->fq;
3546 struct fq_tin *tin = &txqi->tin;
3547 struct ieee80211_tx_info *info;
3548 struct ieee80211_tx_data tx;
3549 ieee80211_tx_result r;
3550 struct ieee80211_vif *vif = txq->vif;
3551
3552 WARN_ON_ONCE(softirq_count() == 0);
3553
3554 if (!ieee80211_txq_airtime_check(hw, txq))
3555 return NULL;
3556
3557 begin:
3558 spin_lock_bh(&fq->lock);
3559
3560 if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ||
3561 test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags))
3562 goto out;
3563
3564 if (vif->txqs_stopped[ieee80211_ac_from_tid(txq->tid)]) {
3565 set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags);
3566 goto out;
3567 }
3568
3569 /* Make sure fragments stay together. */
3570 skb = __skb_dequeue(&txqi->frags);
3571 if (skb)
3572 goto out;
3573
3574 skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
3575 if (!skb)
3576 goto out;
3577
3578 spin_unlock_bh(&fq->lock);
3579
3580 hdr = (struct ieee80211_hdr *)skb->data;
3581 info = IEEE80211_SKB_CB(skb);
3582
3583 memset(&tx, 0, sizeof(tx));
3584 __skb_queue_head_init(&tx.skbs);
3585 tx.local = local;
3586 tx.skb = skb;
3587 tx.sdata = vif_to_sdata(info->control.vif);
3588
3589 if (txq->sta)
3590 tx.sta = container_of(txq->sta, struct sta_info, sta);
3591
3592 /*
3593 * The key can be removed while the packet was queued, so need to call
3594 * this here to get the current key.
3595 */
3596 r = ieee80211_tx_h_select_key(&tx);
3597 if (r != TX_CONTINUE) {
3598 ieee80211_free_txskb(&local->hw, skb);
3599 goto begin;
3600 }
3601
3602 if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
3603 info->flags |= IEEE80211_TX_CTL_AMPDU;
3604 else
3605 info->flags &= ~IEEE80211_TX_CTL_AMPDU;
3606
3607 if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
3608 struct sta_info *sta = container_of(txq->sta, struct sta_info,
3609 sta);
3610 u8 pn_offs = 0;
3611
3612 if (tx.key &&
3613 (tx.key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
3614 pn_offs = ieee80211_hdrlen(hdr->frame_control);
3615
3616 ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
3617 tx.key, skb);
3618 } else {
3619 if (invoke_tx_handlers_late(&tx))
3620 goto begin;
3621
3622 skb = __skb_dequeue(&tx.skbs);
3623
3624 if (!skb_queue_empty(&tx.skbs)) {
3625 spin_lock_bh(&fq->lock);
3626 skb_queue_splice_tail(&tx.skbs, &txqi->frags);
3627 spin_unlock_bh(&fq->lock);
3628 }
3629 }
3630
3631 if (skb_has_frag_list(skb) &&
3632 !ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) {
3633 if (skb_linearize(skb)) {
3634 ieee80211_free_txskb(&local->hw, skb);
3635 goto begin;
3636 }
3637 }
3638
3639 switch (tx.sdata->vif.type) {
3640 case NL80211_IFTYPE_MONITOR:
3641 if (tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
3642 vif = &tx.sdata->vif;
3643 break;
3644 }
3645 tx.sdata = rcu_dereference(local->monitor_sdata);
3646 if (tx.sdata) {
3647 vif = &tx.sdata->vif;
3648 info->hw_queue =
3649 vif->hw_queue[skb_get_queue_mapping(skb)];
3650 } else if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
3651 ieee80211_free_txskb(&local->hw, skb);
3652 goto begin;
3653 } else {
3654 vif = NULL;
3655 }
3656 break;
3657 case NL80211_IFTYPE_AP_VLAN:
3658 tx.sdata = container_of(tx.sdata->bss,
3659 struct ieee80211_sub_if_data, u.ap);
3660 /* fall through */
3661 default:
3662 vif = &tx.sdata->vif;
3663 break;
3664 }
3665
3666 IEEE80211_SKB_CB(skb)->control.vif = vif;
3667
3668 if (local->airtime_flags & AIRTIME_USE_AQL) {
3669 u32 airtime;
3670
> 3671 airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
3672 skb->len);
3673 if (airtime) {
3674 /* We only have 10 bits in tx_time_est, so store airtime
3675 * in increments of 4us and clamp the maximum to 2**12-1
3676 */
3677 airtime = min_t(u32, airtime, 4095) & ~3U;
3678 info->tx_time_est = airtime >> 2;
3679 ieee80211_sta_update_pending_airtime(local, tx.sta,
3680 txq->ac, airtime,
3681 false);
3682 }
3683 }
3684
3685 return skb;
3686
3687 out:
3688 spin_unlock_bh(&fq->lock);
3689
3690 return skb;
3691 }
3692 EXPORT_SYMBOL(ieee80211_tx_dequeue);
3693
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/[email protected] Intel Corporation