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