403 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.4 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.5 */6 7#include "wil6210.h"8#include "txrx.h"9 10#define SEQ_MODULO 0x100011#define SEQ_MASK 0xfff12 13static inline int seq_less(u16 sq1, u16 sq2)14{15 return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);16}17 18static inline u16 seq_inc(u16 sq)19{20 return (sq + 1) & SEQ_MASK;21}22 23static inline u16 seq_sub(u16 sq1, u16 sq2)24{25 return (sq1 - sq2) & SEQ_MASK;26}27 28static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq)29{30 return seq_sub(seq, r->ssn) % r->buf_size;31}32 33static void wil_release_reorder_frame(struct net_device *ndev,34 struct wil_tid_ampdu_rx *r,35 int index)36{37 struct sk_buff *skb = r->reorder_buf[index];38 39 if (!skb)40 goto no_frame;41 42 /* release the frame from the reorder ring buffer */43 r->stored_mpdu_num--;44 r->reorder_buf[index] = NULL;45 wil_netif_rx_any(skb, ndev);46 47no_frame:48 r->head_seq_num = seq_inc(r->head_seq_num);49}50 51static void wil_release_reorder_frames(struct net_device *ndev,52 struct wil_tid_ampdu_rx *r,53 u16 hseq)54{55 int index;56 57 /* note: this function is never called with58 * hseq preceding r->head_seq_num, i.e it is always true59 * !seq_less(hseq, r->head_seq_num)60 * and thus on loop exit it should be61 * r->head_seq_num == hseq62 */63 while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) {64 index = reorder_index(r, r->head_seq_num);65 wil_release_reorder_frame(ndev, r, index);66 }67 r->head_seq_num = hseq;68}69 70static void wil_reorder_release(struct net_device *ndev,71 struct wil_tid_ampdu_rx *r)72{73 int index = reorder_index(r, r->head_seq_num);74 75 while (r->reorder_buf[index]) {76 wil_release_reorder_frame(ndev, r, index);77 index = reorder_index(r, r->head_seq_num);78 }79}80 81/* called in NAPI context */82void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)83__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)84{85 struct wil6210_vif *vif;86 struct net_device *ndev;87 int tid, cid, mid, mcast, retry;88 u16 seq;89 struct wil_sta_info *sta;90 struct wil_tid_ampdu_rx *r;91 u16 hseq;92 int index;93 94 wil->txrx_ops.get_reorder_params(wil, skb, &tid, &cid, &mid, &seq,95 &mcast, &retry);96 sta = &wil->sta[cid];97 98 wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x mcast %01x\n",99 mid, cid, tid, seq, mcast);100 101 vif = wil->vifs[mid];102 if (unlikely(!vif)) {103 wil_dbg_txrx(wil, "invalid VIF, mid %d\n", mid);104 dev_kfree_skb(skb);105 return;106 }107 ndev = vif_to_ndev(vif);108 109 spin_lock(&sta->tid_rx_lock);110 111 r = sta->tid_rx[tid];112 if (!r) {113 wil_netif_rx_any(skb, ndev);114 goto out;115 }116 117 if (unlikely(mcast)) {118 if (retry && seq == r->mcast_last_seq) {119 r->drop_dup_mcast++;120 wil_dbg_txrx(wil, "Rx drop: dup mcast seq 0x%03x\n",121 seq);122 dev_kfree_skb(skb);123 goto out;124 }125 r->mcast_last_seq = seq;126 wil_netif_rx_any(skb, ndev);127 goto out;128 }129 130 r->total++;131 hseq = r->head_seq_num;132 133 /** Due to the race between WMI events, where BACK establishment134 * reported, and data Rx, few packets may be pass up before reorder135 * buffer get allocated. Catch up by pretending SSN is what we136 * see in the 1-st Rx packet137 *138 * Another scenario, Rx get delayed and we got packet from before139 * BACK. Pass it to the stack and wait.140 */141 if (r->first_time) {142 r->first_time = false;143 if (seq != r->head_seq_num) {144 if (seq_less(seq, r->head_seq_num)) {145 wil_err(wil,146 "Error: frame with early sequence 0x%03x, should be 0x%03x. Waiting...\n",147 seq, r->head_seq_num);148 r->first_time = true;149 wil_netif_rx_any(skb, ndev);150 goto out;151 }152 wil_err(wil,153 "Error: 1-st frame with wrong sequence 0x%03x, should be 0x%03x. Fixing...\n",154 seq, r->head_seq_num);155 r->head_seq_num = seq;156 r->ssn = seq;157 }158 }159 160 /* frame with out of date sequence number */161 if (seq_less(seq, r->head_seq_num)) {162 r->ssn_last_drop = seq;163 r->drop_old++;164 wil_dbg_txrx(wil, "Rx drop: old seq 0x%03x head 0x%03x\n",165 seq, r->head_seq_num);166 dev_kfree_skb(skb);167 goto out;168 }169 170 /*171 * If frame the sequence number exceeds our buffering window172 * size release some previous frames to make room for this one.173 */174 if (!seq_less(seq, r->head_seq_num + r->buf_size)) {175 hseq = seq_inc(seq_sub(seq, r->buf_size));176 /* release stored frames up to new head to stack */177 wil_release_reorder_frames(ndev, r, hseq);178 }179 180 /* Now the new frame is always in the range of the reordering buffer */181 182 index = reorder_index(r, seq);183 184 /* check if we already stored this frame */185 if (r->reorder_buf[index]) {186 r->drop_dup++;187 wil_dbg_txrx(wil, "Rx drop: dup seq 0x%03x\n", seq);188 dev_kfree_skb(skb);189 goto out;190 }191 192 /*193 * If the current MPDU is in the right order and nothing else194 * is stored we can process it directly, no need to buffer it.195 * If it is first but there's something stored, we may be able196 * to release frames after this one.197 */198 if (seq == r->head_seq_num && r->stored_mpdu_num == 0) {199 r->head_seq_num = seq_inc(r->head_seq_num);200 wil_netif_rx_any(skb, ndev);201 goto out;202 }203 204 /* put the frame in the reordering buffer */205 r->reorder_buf[index] = skb;206 r->stored_mpdu_num++;207 wil_reorder_release(ndev, r);208 209out:210 spin_unlock(&sta->tid_rx_lock);211}212 213/* process BAR frame, called in NAPI context */214void wil_rx_bar(struct wil6210_priv *wil, struct wil6210_vif *vif,215 u8 cid, u8 tid, u16 seq)216{217 struct wil_sta_info *sta = &wil->sta[cid];218 struct net_device *ndev = vif_to_ndev(vif);219 struct wil_tid_ampdu_rx *r;220 221 spin_lock(&sta->tid_rx_lock);222 223 r = sta->tid_rx[tid];224 if (!r) {225 wil_err(wil, "BAR for non-existing CID %d TID %d\n", cid, tid);226 goto out;227 }228 if (seq_less(seq, r->head_seq_num)) {229 wil_err(wil, "BAR Seq 0x%03x preceding head 0x%03x\n",230 seq, r->head_seq_num);231 goto out;232 }233 wil_dbg_txrx(wil, "BAR: CID %d MID %d TID %d Seq 0x%03x head 0x%03x\n",234 cid, vif->mid, tid, seq, r->head_seq_num);235 wil_release_reorder_frames(ndev, r, seq);236 237out:238 spin_unlock(&sta->tid_rx_lock);239}240 241struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,242 int size, u16 ssn)243{244 struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL);245 246 if (!r)247 return NULL;248 249 r->reorder_buf =250 kcalloc(size, sizeof(struct sk_buff *), GFP_KERNEL);251 if (!r->reorder_buf) {252 kfree(r);253 return NULL;254 }255 256 r->ssn = ssn;257 r->head_seq_num = ssn;258 r->buf_size = size;259 r->stored_mpdu_num = 0;260 r->first_time = true;261 r->mcast_last_seq = U16_MAX;262 return r;263}264 265void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,266 struct wil_tid_ampdu_rx *r)267{268 int i;269 270 if (!r)271 return;272 273 /* Do not pass remaining frames to the network stack - it may be274 * not expecting to get any more Rx. Rx from here may lead to275 * kernel OOPS since some per-socket accounting info was already276 * released.277 */278 for (i = 0; i < r->buf_size; i++)279 kfree_skb(r->reorder_buf[i]);280 281 kfree(r->reorder_buf);282 kfree(r);283}284 285/* ADDBA processing */286static u16 wil_agg_size(struct wil6210_priv *wil, u16 req_agg_wsize)287{288 u16 max_agg_size = min_t(u16, wil->max_agg_wsize, wil->max_ampdu_size /289 (mtu_max + WIL_MAX_MPDU_OVERHEAD));290 291 if (!req_agg_wsize)292 return max_agg_size;293 294 return min(max_agg_size, req_agg_wsize);295}296 297/* Block Ack - Rx side (recipient) */298int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid,299 u8 dialog_token, __le16 ba_param_set,300 __le16 ba_timeout, __le16 ba_seq_ctrl)301__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)302{303 u16 param_set = le16_to_cpu(ba_param_set);304 u16 agg_timeout = le16_to_cpu(ba_timeout);305 u16 seq_ctrl = le16_to_cpu(ba_seq_ctrl);306 struct wil_sta_info *sta;307 u16 agg_wsize;308 /* bit 0: A-MSDU supported309 * bit 1: policy (should be 0 for us)310 * bits 2..5: TID311 * bits 6..15: buffer size312 */313 u16 req_agg_wsize = WIL_GET_BITS(param_set, 6, 15);314 bool agg_amsdu = wil->use_enhanced_dma_hw &&315 wil->use_rx_hw_reordering &&316 test_bit(WMI_FW_CAPABILITY_AMSDU, wil->fw_capabilities) &&317 wil->amsdu_en && (param_set & BIT(0));318 int ba_policy = param_set & BIT(1);319 u16 ssn = seq_ctrl >> 4;320 struct wil_tid_ampdu_rx *r;321 int rc = 0;322 323 might_sleep();324 325 /* sanity checks */326 if (cid >= wil->max_assoc_sta) {327 wil_err(wil, "BACK: invalid CID %d\n", cid);328 rc = -EINVAL;329 goto out;330 }331 332 sta = &wil->sta[cid];333 if (sta->status != wil_sta_connected) {334 wil_err(wil, "BACK: CID %d not connected\n", cid);335 rc = -EINVAL;336 goto out;337 }338 339 wil_dbg_wmi(wil,340 "ADDBA request for CID %d %pM TID %d size %d timeout %d AMSDU%s policy %d token %d SSN 0x%03x\n",341 cid, sta->addr, tid, req_agg_wsize, agg_timeout,342 agg_amsdu ? "+" : "-", !!ba_policy, dialog_token, ssn);343 344 /* apply policies */345 if (req_agg_wsize == 0) {346 wil_dbg_misc(wil, "Suggest BACK wsize %d\n",347 wil->max_agg_wsize);348 agg_wsize = wil->max_agg_wsize;349 } else {350 agg_wsize = min_t(u16, wil->max_agg_wsize, req_agg_wsize);351 }352 353 rc = wil->txrx_ops.wmi_addba_rx_resp(wil, mid, cid, tid, dialog_token,354 WLAN_STATUS_SUCCESS, agg_amsdu,355 agg_wsize, agg_timeout);356 if (rc) {357 wil_err(wil, "do not apply ba, rc(%d)\n", rc);358 goto out;359 }360 361 /* apply */362 if (!wil->use_rx_hw_reordering) {363 r = wil_tid_ampdu_rx_alloc(wil, agg_wsize, ssn);364 spin_lock_bh(&sta->tid_rx_lock);365 wil_tid_ampdu_rx_free(wil, sta->tid_rx[tid]);366 sta->tid_rx[tid] = r;367 spin_unlock_bh(&sta->tid_rx_lock);368 }369 370out:371 return rc;372}373 374/* BACK - Tx side (originator) */375int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize)376{377 u8 agg_wsize = wil_agg_size(wil, wsize);378 u16 agg_timeout = 0;379 struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ringid];380 int rc = 0;381 382 if (txdata->addba_in_progress) {383 wil_dbg_misc(wil, "ADDBA for vring[%d] already in progress\n",384 ringid);385 goto out;386 }387 if (txdata->agg_wsize) {388 wil_dbg_misc(wil,389 "ADDBA for vring[%d] already done for wsize %d\n",390 ringid, txdata->agg_wsize);391 goto out;392 }393 txdata->addba_in_progress = true;394 rc = wmi_addba(wil, txdata->mid, ringid, agg_wsize, agg_timeout);395 if (rc) {396 wil_err(wil, "wmi_addba failed, rc (%d)", rc);397 txdata->addba_in_progress = false;398 }399 400out:401 return rc;402}403