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