162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2018 Felix Fietkau <nbd@nbd.name> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include "mt76.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_cistatic unsigned long mt76_aggr_tid_to_timeo(u8 tidno) 862306a36Sopenharmony_ci{ 962306a36Sopenharmony_ci /* Currently voice traffic (AC_VO) always runs without aggregation, 1062306a36Sopenharmony_ci * no special handling is needed. AC_BE/AC_BK use tids 0-3. Just check 1162306a36Sopenharmony_ci * for non AC_BK/AC_BE and set smaller timeout for it. */ 1262306a36Sopenharmony_ci return HZ / (tidno >= 4 ? 25 : 10); 1362306a36Sopenharmony_ci} 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic void 1662306a36Sopenharmony_cimt76_aggr_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames, int idx) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct sk_buff *skb; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci tid->head = ieee80211_sn_inc(tid->head); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci skb = tid->reorder_buf[idx]; 2362306a36Sopenharmony_ci if (!skb) 2462306a36Sopenharmony_ci return; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci tid->reorder_buf[idx] = NULL; 2762306a36Sopenharmony_ci tid->nframes--; 2862306a36Sopenharmony_ci __skb_queue_tail(frames, skb); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void 3262306a36Sopenharmony_cimt76_rx_aggr_release_frames(struct mt76_rx_tid *tid, 3362306a36Sopenharmony_ci struct sk_buff_head *frames, 3462306a36Sopenharmony_ci u16 head) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci int idx; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci while (ieee80211_sn_less(tid->head, head)) { 3962306a36Sopenharmony_ci idx = tid->head % tid->size; 4062306a36Sopenharmony_ci mt76_aggr_release(tid, frames, idx); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void 4562306a36Sopenharmony_cimt76_rx_aggr_release_head(struct mt76_rx_tid *tid, struct sk_buff_head *frames) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int idx = tid->head % tid->size; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci while (tid->reorder_buf[idx]) { 5062306a36Sopenharmony_ci mt76_aggr_release(tid, frames, idx); 5162306a36Sopenharmony_ci idx = tid->head % tid->size; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void 5662306a36Sopenharmony_cimt76_rx_aggr_check_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct mt76_rx_status *status; 5962306a36Sopenharmony_ci struct sk_buff *skb; 6062306a36Sopenharmony_ci int start, idx, nframes; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (!tid->nframes) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci start = tid->head % tid->size; 6862306a36Sopenharmony_ci nframes = tid->nframes; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for (idx = (tid->head + 1) % tid->size; 7162306a36Sopenharmony_ci idx != start && nframes; 7262306a36Sopenharmony_ci idx = (idx + 1) % tid->size) { 7362306a36Sopenharmony_ci skb = tid->reorder_buf[idx]; 7462306a36Sopenharmony_ci if (!skb) 7562306a36Sopenharmony_ci continue; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci nframes--; 7862306a36Sopenharmony_ci status = (struct mt76_rx_status *)skb->cb; 7962306a36Sopenharmony_ci if (!time_after32(jiffies, 8062306a36Sopenharmony_ci status->reorder_time + 8162306a36Sopenharmony_ci mt76_aggr_tid_to_timeo(tid->num))) 8262306a36Sopenharmony_ci continue; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci mt76_rx_aggr_release_frames(tid, frames, status->seqno); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void 9162306a36Sopenharmony_cimt76_rx_aggr_reorder_work(struct work_struct *work) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct mt76_rx_tid *tid = container_of(work, struct mt76_rx_tid, 9462306a36Sopenharmony_ci reorder_work.work); 9562306a36Sopenharmony_ci struct mt76_dev *dev = tid->dev; 9662306a36Sopenharmony_ci struct sk_buff_head frames; 9762306a36Sopenharmony_ci int nframes; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci __skb_queue_head_init(&frames); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci local_bh_disable(); 10262306a36Sopenharmony_ci rcu_read_lock(); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci spin_lock(&tid->lock); 10562306a36Sopenharmony_ci mt76_rx_aggr_check_release(tid, &frames); 10662306a36Sopenharmony_ci nframes = tid->nframes; 10762306a36Sopenharmony_ci spin_unlock(&tid->lock); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (nframes) 11062306a36Sopenharmony_ci ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, 11162306a36Sopenharmony_ci mt76_aggr_tid_to_timeo(tid->num)); 11262306a36Sopenharmony_ci mt76_rx_complete(dev, &frames, NULL); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci rcu_read_unlock(); 11562306a36Sopenharmony_ci local_bh_enable(); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void 11962306a36Sopenharmony_cimt76_rx_aggr_check_ctl(struct sk_buff *skb, struct sk_buff_head *frames) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; 12262306a36Sopenharmony_ci struct ieee80211_bar *bar = mt76_skb_get_hdr(skb); 12362306a36Sopenharmony_ci struct mt76_wcid *wcid = status->wcid; 12462306a36Sopenharmony_ci struct mt76_rx_tid *tid; 12562306a36Sopenharmony_ci u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK; 12662306a36Sopenharmony_ci u16 seqno; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!ieee80211_is_ctl(bar->frame_control)) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (!ieee80211_is_back_req(bar->frame_control)) 13262306a36Sopenharmony_ci return; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci status->qos_ctl = tidno = le16_to_cpu(bar->control) >> 12; 13562306a36Sopenharmony_ci seqno = IEEE80211_SEQ_TO_SN(le16_to_cpu(bar->start_seq_num)); 13662306a36Sopenharmony_ci tid = rcu_dereference(wcid->aggr[tidno]); 13762306a36Sopenharmony_ci if (!tid) 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci spin_lock_bh(&tid->lock); 14162306a36Sopenharmony_ci if (!tid->stopped) { 14262306a36Sopenharmony_ci mt76_rx_aggr_release_frames(tid, frames, seqno); 14362306a36Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci spin_unlock_bh(&tid->lock); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_civoid mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; 15162306a36Sopenharmony_ci struct mt76_wcid *wcid = status->wcid; 15262306a36Sopenharmony_ci struct ieee80211_sta *sta; 15362306a36Sopenharmony_ci struct mt76_rx_tid *tid; 15462306a36Sopenharmony_ci bool sn_less; 15562306a36Sopenharmony_ci u16 seqno, head, size, idx; 15662306a36Sopenharmony_ci u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK; 15762306a36Sopenharmony_ci u8 ackp; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci __skb_queue_tail(frames, skb); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci sta = wcid_to_sta(wcid); 16262306a36Sopenharmony_ci if (!sta) 16362306a36Sopenharmony_ci return; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!status->aggr) { 16662306a36Sopenharmony_ci if (!(status->flag & RX_FLAG_8023)) 16762306a36Sopenharmony_ci mt76_rx_aggr_check_ctl(skb, frames); 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* not part of a BA session */ 17262306a36Sopenharmony_ci ackp = status->qos_ctl & IEEE80211_QOS_CTL_ACK_POLICY_MASK; 17362306a36Sopenharmony_ci if (ackp == IEEE80211_QOS_CTL_ACK_POLICY_NOACK) 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci tid = rcu_dereference(wcid->aggr[tidno]); 17762306a36Sopenharmony_ci if (!tid) 17862306a36Sopenharmony_ci return; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci status->flag |= RX_FLAG_DUP_VALIDATED; 18162306a36Sopenharmony_ci spin_lock_bh(&tid->lock); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (tid->stopped) 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci head = tid->head; 18762306a36Sopenharmony_ci seqno = status->seqno; 18862306a36Sopenharmony_ci size = tid->size; 18962306a36Sopenharmony_ci sn_less = ieee80211_sn_less(seqno, head); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (!tid->started) { 19262306a36Sopenharmony_ci if (sn_less) 19362306a36Sopenharmony_ci goto out; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci tid->started = true; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (sn_less) { 19962306a36Sopenharmony_ci __skb_unlink(skb, frames); 20062306a36Sopenharmony_ci dev_kfree_skb(skb); 20162306a36Sopenharmony_ci goto out; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (seqno == head) { 20562306a36Sopenharmony_ci tid->head = ieee80211_sn_inc(head); 20662306a36Sopenharmony_ci if (tid->nframes) 20762306a36Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 20862306a36Sopenharmony_ci goto out; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci __skb_unlink(skb, frames); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * Frame sequence number exceeds buffering window, free up some space 21562306a36Sopenharmony_ci * by releasing previous frames 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci if (!ieee80211_sn_less(seqno, head + size)) { 21862306a36Sopenharmony_ci head = ieee80211_sn_inc(ieee80211_sn_sub(seqno, size)); 21962306a36Sopenharmony_ci mt76_rx_aggr_release_frames(tid, frames, head); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci idx = seqno % size; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Discard if the current slot is already in use */ 22562306a36Sopenharmony_ci if (tid->reorder_buf[idx]) { 22662306a36Sopenharmony_ci dev_kfree_skb(skb); 22762306a36Sopenharmony_ci goto out; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci status->reorder_time = jiffies; 23162306a36Sopenharmony_ci tid->reorder_buf[idx] = skb; 23262306a36Sopenharmony_ci tid->nframes++; 23362306a36Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, 23662306a36Sopenharmony_ci mt76_aggr_tid_to_timeo(tid->num)); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ciout: 23962306a36Sopenharmony_ci spin_unlock_bh(&tid->lock); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciint mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno, 24362306a36Sopenharmony_ci u16 ssn, u16 size) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct mt76_rx_tid *tid; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci mt76_rx_aggr_stop(dev, wcid, tidno); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci tid = kzalloc(struct_size(tid, reorder_buf, size), GFP_KERNEL); 25062306a36Sopenharmony_ci if (!tid) 25162306a36Sopenharmony_ci return -ENOMEM; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci tid->dev = dev; 25462306a36Sopenharmony_ci tid->head = ssn; 25562306a36Sopenharmony_ci tid->size = size; 25662306a36Sopenharmony_ci tid->num = tidno; 25762306a36Sopenharmony_ci INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work); 25862306a36Sopenharmony_ci spin_lock_init(&tid->lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci rcu_assign_pointer(wcid->aggr[tidno], tid); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_rx_aggr_start); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci u16 size = tid->size; 26962306a36Sopenharmony_ci int i; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_lock_bh(&tid->lock); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci tid->stopped = true; 27462306a36Sopenharmony_ci for (i = 0; tid->nframes && i < size; i++) { 27562306a36Sopenharmony_ci struct sk_buff *skb = tid->reorder_buf[i]; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!skb) 27862306a36Sopenharmony_ci continue; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci tid->reorder_buf[i] = NULL; 28162306a36Sopenharmony_ci tid->nframes--; 28262306a36Sopenharmony_ci dev_kfree_skb(skb); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci spin_unlock_bh(&tid->lock); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci cancel_delayed_work_sync(&tid->reorder_work); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_civoid mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct mt76_rx_tid *tid = NULL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci tid = rcu_replace_pointer(wcid->aggr[tidno], tid, 29562306a36Sopenharmony_ci lockdep_is_held(&dev->mutex)); 29662306a36Sopenharmony_ci if (tid) { 29762306a36Sopenharmony_ci mt76_rx_aggr_shutdown(dev, tid); 29862306a36Sopenharmony_ci kfree_rcu(tid, rcu_head); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_rx_aggr_stop); 302