18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Felix Fietkau <nbd@nbd.name> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include "mt76.h" 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_cistatic unsigned long mt76_aggr_tid_to_timeo(u8 tidno) 88c2ecf20Sopenharmony_ci{ 98c2ecf20Sopenharmony_ci /* Currently voice traffic (AC_VO) always runs without aggregation, 108c2ecf20Sopenharmony_ci * no special handling is needed. AC_BE/AC_BK use tids 0-3. Just check 118c2ecf20Sopenharmony_ci * for non AC_BK/AC_BE and set smaller timeout for it. */ 128c2ecf20Sopenharmony_ci return HZ / (tidno >= 4 ? 25 : 10); 138c2ecf20Sopenharmony_ci} 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic void 168c2ecf20Sopenharmony_cimt76_aggr_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames, int idx) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci struct sk_buff *skb; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci tid->head = ieee80211_sn_inc(tid->head); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci skb = tid->reorder_buf[idx]; 238c2ecf20Sopenharmony_ci if (!skb) 248c2ecf20Sopenharmony_ci return; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci tid->reorder_buf[idx] = NULL; 278c2ecf20Sopenharmony_ci tid->nframes--; 288c2ecf20Sopenharmony_ci __skb_queue_tail(frames, skb); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void 328c2ecf20Sopenharmony_cimt76_rx_aggr_release_frames(struct mt76_rx_tid *tid, 338c2ecf20Sopenharmony_ci struct sk_buff_head *frames, 348c2ecf20Sopenharmony_ci u16 head) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci int idx; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci while (ieee80211_sn_less(tid->head, head)) { 398c2ecf20Sopenharmony_ci idx = tid->head % tid->size; 408c2ecf20Sopenharmony_ci mt76_aggr_release(tid, frames, idx); 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void 458c2ecf20Sopenharmony_cimt76_rx_aggr_release_head(struct mt76_rx_tid *tid, struct sk_buff_head *frames) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci int idx = tid->head % tid->size; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci while (tid->reorder_buf[idx]) { 508c2ecf20Sopenharmony_ci mt76_aggr_release(tid, frames, idx); 518c2ecf20Sopenharmony_ci idx = tid->head % tid->size; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void 568c2ecf20Sopenharmony_cimt76_rx_aggr_check_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct mt76_rx_status *status; 598c2ecf20Sopenharmony_ci struct sk_buff *skb; 608c2ecf20Sopenharmony_ci int start, idx, nframes; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (!tid->nframes) 638c2ecf20Sopenharmony_ci return; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci start = tid->head % tid->size; 688c2ecf20Sopenharmony_ci nframes = tid->nframes; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci for (idx = (tid->head + 1) % tid->size; 718c2ecf20Sopenharmony_ci idx != start && nframes; 728c2ecf20Sopenharmony_ci idx = (idx + 1) % tid->size) { 738c2ecf20Sopenharmony_ci skb = tid->reorder_buf[idx]; 748c2ecf20Sopenharmony_ci if (!skb) 758c2ecf20Sopenharmony_ci continue; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci nframes--; 788c2ecf20Sopenharmony_ci status = (struct mt76_rx_status *)skb->cb; 798c2ecf20Sopenharmony_ci if (!time_after(jiffies, 808c2ecf20Sopenharmony_ci status->reorder_time + 818c2ecf20Sopenharmony_ci mt76_aggr_tid_to_timeo(tid->num))) 828c2ecf20Sopenharmony_ci continue; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci mt76_rx_aggr_release_frames(tid, frames, status->seqno); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void 918c2ecf20Sopenharmony_cimt76_rx_aggr_reorder_work(struct work_struct *work) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct mt76_rx_tid *tid = container_of(work, struct mt76_rx_tid, 948c2ecf20Sopenharmony_ci reorder_work.work); 958c2ecf20Sopenharmony_ci struct mt76_dev *dev = tid->dev; 968c2ecf20Sopenharmony_ci struct sk_buff_head frames; 978c2ecf20Sopenharmony_ci int nframes; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci __skb_queue_head_init(&frames); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci local_bh_disable(); 1028c2ecf20Sopenharmony_ci rcu_read_lock(); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci spin_lock(&tid->lock); 1058c2ecf20Sopenharmony_ci mt76_rx_aggr_check_release(tid, &frames); 1068c2ecf20Sopenharmony_ci nframes = tid->nframes; 1078c2ecf20Sopenharmony_ci spin_unlock(&tid->lock); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (nframes) 1108c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, 1118c2ecf20Sopenharmony_ci mt76_aggr_tid_to_timeo(tid->num)); 1128c2ecf20Sopenharmony_ci mt76_rx_complete(dev, &frames, NULL); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci rcu_read_unlock(); 1158c2ecf20Sopenharmony_ci local_bh_enable(); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void 1198c2ecf20Sopenharmony_cimt76_rx_aggr_check_ctl(struct sk_buff *skb, struct sk_buff_head *frames) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; 1228c2ecf20Sopenharmony_ci struct ieee80211_bar *bar = mt76_skb_get_hdr(skb); 1238c2ecf20Sopenharmony_ci struct mt76_wcid *wcid = status->wcid; 1248c2ecf20Sopenharmony_ci struct mt76_rx_tid *tid; 1258c2ecf20Sopenharmony_ci u16 seqno; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!ieee80211_is_ctl(bar->frame_control)) 1288c2ecf20Sopenharmony_ci return; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!ieee80211_is_back_req(bar->frame_control)) 1318c2ecf20Sopenharmony_ci return; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci status->tid = le16_to_cpu(bar->control) >> 12; 1348c2ecf20Sopenharmony_ci seqno = IEEE80211_SEQ_TO_SN(le16_to_cpu(bar->start_seq_num)); 1358c2ecf20Sopenharmony_ci tid = rcu_dereference(wcid->aggr[status->tid]); 1368c2ecf20Sopenharmony_ci if (!tid) 1378c2ecf20Sopenharmony_ci return; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci spin_lock_bh(&tid->lock); 1408c2ecf20Sopenharmony_ci if (!tid->stopped) { 1418c2ecf20Sopenharmony_ci mt76_rx_aggr_release_frames(tid, frames, seqno); 1428c2ecf20Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci spin_unlock_bh(&tid->lock); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_civoid mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; 1508c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); 1518c2ecf20Sopenharmony_ci struct mt76_wcid *wcid = status->wcid; 1528c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 1538c2ecf20Sopenharmony_ci struct mt76_rx_tid *tid; 1548c2ecf20Sopenharmony_ci bool sn_less; 1558c2ecf20Sopenharmony_ci u16 seqno, head, size, idx; 1568c2ecf20Sopenharmony_ci u8 ackp; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci __skb_queue_tail(frames, skb); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci sta = wcid_to_sta(wcid); 1618c2ecf20Sopenharmony_ci if (!sta) 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!status->aggr) { 1658c2ecf20Sopenharmony_ci mt76_rx_aggr_check_ctl(skb, frames); 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* not part of a BA session */ 1708c2ecf20Sopenharmony_ci ackp = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_ACK_POLICY_MASK; 1718c2ecf20Sopenharmony_ci if (ackp != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK && 1728c2ecf20Sopenharmony_ci ackp != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL) 1738c2ecf20Sopenharmony_ci return; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci tid = rcu_dereference(wcid->aggr[status->tid]); 1768c2ecf20Sopenharmony_ci if (!tid) 1778c2ecf20Sopenharmony_ci return; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci status->flag |= RX_FLAG_DUP_VALIDATED; 1808c2ecf20Sopenharmony_ci spin_lock_bh(&tid->lock); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (tid->stopped) 1838c2ecf20Sopenharmony_ci goto out; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci head = tid->head; 1868c2ecf20Sopenharmony_ci seqno = status->seqno; 1878c2ecf20Sopenharmony_ci size = tid->size; 1888c2ecf20Sopenharmony_ci sn_less = ieee80211_sn_less(seqno, head); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!tid->started) { 1918c2ecf20Sopenharmony_ci if (sn_less) 1928c2ecf20Sopenharmony_ci goto out; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci tid->started = true; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (sn_less) { 1988c2ecf20Sopenharmony_ci __skb_unlink(skb, frames); 1998c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2008c2ecf20Sopenharmony_ci goto out; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (seqno == head) { 2048c2ecf20Sopenharmony_ci tid->head = ieee80211_sn_inc(head); 2058c2ecf20Sopenharmony_ci if (tid->nframes) 2068c2ecf20Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 2078c2ecf20Sopenharmony_ci goto out; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci __skb_unlink(skb, frames); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * Frame sequence number exceeds buffering window, free up some space 2148c2ecf20Sopenharmony_ci * by releasing previous frames 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci if (!ieee80211_sn_less(seqno, head + size)) { 2178c2ecf20Sopenharmony_ci head = ieee80211_sn_inc(ieee80211_sn_sub(seqno, size)); 2188c2ecf20Sopenharmony_ci mt76_rx_aggr_release_frames(tid, frames, head); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci idx = seqno % size; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Discard if the current slot is already in use */ 2248c2ecf20Sopenharmony_ci if (tid->reorder_buf[idx]) { 2258c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2268c2ecf20Sopenharmony_ci goto out; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci status->reorder_time = jiffies; 2308c2ecf20Sopenharmony_ci tid->reorder_buf[idx] = skb; 2318c2ecf20Sopenharmony_ci tid->nframes++; 2328c2ecf20Sopenharmony_ci mt76_rx_aggr_release_head(tid, frames); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, 2358c2ecf20Sopenharmony_ci mt76_aggr_tid_to_timeo(tid->num)); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciout: 2388c2ecf20Sopenharmony_ci spin_unlock_bh(&tid->lock); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ciint mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno, 2428c2ecf20Sopenharmony_ci u16 ssn, u16 size) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct mt76_rx_tid *tid; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci mt76_rx_aggr_stop(dev, wcid, tidno); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci tid = kzalloc(struct_size(tid, reorder_buf, size), GFP_KERNEL); 2498c2ecf20Sopenharmony_ci if (!tid) 2508c2ecf20Sopenharmony_ci return -ENOMEM; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci tid->dev = dev; 2538c2ecf20Sopenharmony_ci tid->head = ssn; 2548c2ecf20Sopenharmony_ci tid->size = size; 2558c2ecf20Sopenharmony_ci tid->num = tidno; 2568c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work); 2578c2ecf20Sopenharmony_ci spin_lock_init(&tid->lock); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci rcu_assign_pointer(wcid->aggr[tidno], tid); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_rx_aggr_start); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci u16 size = tid->size; 2688c2ecf20Sopenharmony_ci int i; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci spin_lock_bh(&tid->lock); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci tid->stopped = true; 2738c2ecf20Sopenharmony_ci for (i = 0; tid->nframes && i < size; i++) { 2748c2ecf20Sopenharmony_ci struct sk_buff *skb = tid->reorder_buf[i]; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (!skb) 2778c2ecf20Sopenharmony_ci continue; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci tid->reorder_buf[i] = NULL; 2808c2ecf20Sopenharmony_ci tid->nframes--; 2818c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci spin_unlock_bh(&tid->lock); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&tid->reorder_work); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_civoid mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct mt76_rx_tid *tid = NULL; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci tid = rcu_replace_pointer(wcid->aggr[tidno], tid, 2948c2ecf20Sopenharmony_ci lockdep_is_held(&dev->mutex)); 2958c2ecf20Sopenharmony_ci if (tid) { 2968c2ecf20Sopenharmony_ci mt76_rx_aggr_shutdown(dev, tid); 2978c2ecf20Sopenharmony_ci kfree_rcu(tid, rcu_head); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_rx_aggr_stop); 301