Path: blob/main/sys/contrib/dev/mediatek/mt76/agg-rx.c
48378 views
// SPDX-License-Identifier: ISC1/*2* Copyright (C) 2018 Felix Fietkau <[email protected]>3*/4#include "mt76.h"56static unsigned long mt76_aggr_tid_to_timeo(u8 tidno)7{8/* Currently voice traffic (AC_VO) always runs without aggregation,9* no special handling is needed. AC_BE/AC_BK use tids 0-3. Just check10* for non AC_BK/AC_BE and set smaller timeout for it. */11return HZ / (tidno >= 4 ? 25 : 10);12}1314static void15mt76_aggr_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames, int idx)16{17struct sk_buff *skb;1819tid->head = ieee80211_sn_inc(tid->head);2021skb = tid->reorder_buf[idx];22if (!skb)23return;2425tid->reorder_buf[idx] = NULL;26tid->nframes--;27__skb_queue_tail(frames, skb);28}2930static void31mt76_rx_aggr_release_frames(struct mt76_rx_tid *tid,32struct sk_buff_head *frames,33u16 head)34{35int idx;3637while (ieee80211_sn_less(tid->head, head)) {38idx = tid->head % tid->size;39mt76_aggr_release(tid, frames, idx);40}41}4243static void44mt76_rx_aggr_release_head(struct mt76_rx_tid *tid, struct sk_buff_head *frames)45{46int idx = tid->head % tid->size;4748while (tid->reorder_buf[idx]) {49mt76_aggr_release(tid, frames, idx);50idx = tid->head % tid->size;51}52}5354static void55mt76_rx_aggr_check_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames)56{57struct mt76_rx_status *status;58struct sk_buff *skb;59int start, idx, nframes;6061if (!tid->nframes)62return;6364mt76_rx_aggr_release_head(tid, frames);6566start = tid->head % tid->size;67nframes = tid->nframes;6869for (idx = (tid->head + 1) % tid->size;70idx != start && nframes;71idx = (idx + 1) % tid->size) {72skb = tid->reorder_buf[idx];73if (!skb)74continue;7576nframes--;77status = (struct mt76_rx_status *)skb->cb;78if (!time_after32(jiffies,79status->reorder_time +80mt76_aggr_tid_to_timeo(tid->num)))81continue;8283mt76_rx_aggr_release_frames(tid, frames, status->seqno);84}8586mt76_rx_aggr_release_head(tid, frames);87}8889static void90mt76_rx_aggr_reorder_work(struct work_struct *work)91{92struct mt76_rx_tid *tid = container_of(work, struct mt76_rx_tid,93reorder_work.work);94struct mt76_dev *dev = tid->dev;95struct sk_buff_head frames;96int nframes;9798__skb_queue_head_init(&frames);99100local_bh_disable();101rcu_read_lock();102103spin_lock(&tid->lock);104mt76_rx_aggr_check_release(tid, &frames);105nframes = tid->nframes;106spin_unlock(&tid->lock);107108if (nframes)109ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work,110mt76_aggr_tid_to_timeo(tid->num));111mt76_rx_complete(dev, &frames, NULL);112113rcu_read_unlock();114local_bh_enable();115}116117static void118mt76_rx_aggr_check_ctl(struct sk_buff *skb, struct sk_buff_head *frames)119{120struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;121struct ieee80211_bar *bar = mt76_skb_get_hdr(skb);122struct mt76_wcid *wcid = status->wcid;123struct mt76_rx_tid *tid;124u8 tidno;125u16 seqno;126127if (!ieee80211_is_ctl(bar->frame_control))128return;129130if (!ieee80211_is_back_req(bar->frame_control))131return;132133status->qos_ctl = tidno = le16_to_cpu(bar->control) >> 12;134seqno = IEEE80211_SEQ_TO_SN(le16_to_cpu(bar->start_seq_num));135tid = rcu_dereference(wcid->aggr[tidno]);136if (!tid)137return;138139spin_lock_bh(&tid->lock);140if (!tid->stopped) {141mt76_rx_aggr_release_frames(tid, frames, seqno);142mt76_rx_aggr_release_head(tid, frames);143}144spin_unlock_bh(&tid->lock);145}146147void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)148{149struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;150struct mt76_wcid *wcid = status->wcid;151struct ieee80211_sta *sta;152struct mt76_rx_tid *tid;153bool sn_less;154u16 seqno, head, size, idx;155u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;156u8 ackp;157158__skb_queue_tail(frames, skb);159160sta = wcid_to_sta(wcid);161if (!sta)162return;163164if (!status->aggr) {165if (!(status->flag & RX_FLAG_8023))166mt76_rx_aggr_check_ctl(skb, frames);167return;168}169170/* not part of a BA session */171ackp = status->qos_ctl & IEEE80211_QOS_CTL_ACK_POLICY_MASK;172if (ackp == IEEE80211_QOS_CTL_ACK_POLICY_NOACK)173return;174175tid = rcu_dereference(wcid->aggr[tidno]);176if (!tid)177return;178179status->flag |= RX_FLAG_DUP_VALIDATED;180spin_lock_bh(&tid->lock);181182if (tid->stopped)183goto out;184185head = tid->head;186seqno = status->seqno;187size = tid->size;188sn_less = ieee80211_sn_less(seqno, head);189190if (!tid->started) {191if (sn_less)192goto out;193194tid->started = true;195}196197if (sn_less) {198__skb_unlink(skb, frames);199dev_kfree_skb(skb);200goto out;201}202203if (seqno == head) {204tid->head = ieee80211_sn_inc(head);205if (tid->nframes)206mt76_rx_aggr_release_head(tid, frames);207goto out;208}209210__skb_unlink(skb, frames);211212/*213* Frame sequence number exceeds buffering window, free up some space214* by releasing previous frames215*/216if (!ieee80211_sn_less(seqno, head + size)) {217head = ieee80211_sn_inc(ieee80211_sn_sub(seqno, size));218mt76_rx_aggr_release_frames(tid, frames, head);219}220221idx = seqno % size;222223/* Discard if the current slot is already in use */224if (tid->reorder_buf[idx]) {225dev_kfree_skb(skb);226goto out;227}228229status->reorder_time = jiffies;230tid->reorder_buf[idx] = skb;231tid->nframes++;232mt76_rx_aggr_release_head(tid, frames);233234ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work,235mt76_aggr_tid_to_timeo(tid->num));236237out:238spin_unlock_bh(&tid->lock);239}240241int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno,242u16 ssn, u16 size)243{244struct mt76_rx_tid *tid;245246mt76_rx_aggr_stop(dev, wcid, tidno);247248tid = kzalloc(struct_size(tid, reorder_buf, size), GFP_KERNEL);249if (!tid)250return -ENOMEM;251252tid->dev = dev;253tid->head = ssn;254tid->size = size;255tid->num = tidno;256INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work);257spin_lock_init(&tid->lock);258259rcu_assign_pointer(wcid->aggr[tidno], tid);260261return 0;262}263EXPORT_SYMBOL_GPL(mt76_rx_aggr_start);264265static void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid)266{267u16 size = tid->size;268int i;269270spin_lock_bh(&tid->lock);271272tid->stopped = true;273for (i = 0; tid->nframes && i < size; i++) {274struct sk_buff *skb = tid->reorder_buf[i];275276if (!skb)277continue;278279tid->reorder_buf[i] = NULL;280tid->nframes--;281dev_kfree_skb(skb);282}283284spin_unlock_bh(&tid->lock);285286cancel_delayed_work_sync(&tid->reorder_work);287}288289void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno)290{291struct mt76_rx_tid *tid = NULL;292293tid = rcu_replace_pointer(wcid->aggr[tidno], tid,294lockdep_is_held(&dev->mutex));295if (tid) {296mt76_rx_aggr_shutdown(dev, tid);297kfree_rcu(tid, rcu_head);298}299}300EXPORT_SYMBOL_GPL(mt76_rx_aggr_stop);301302303