root/drivers/net/wireless/ath/wil6210/rx_reorder.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. seq_less
  2. seq_inc
  3. seq_sub
  4. reorder_index
  5. wil_release_reorder_frame
  6. wil_release_reorder_frames
  7. wil_reorder_release
  8. wil_rx_reorder
  9. wil_rx_bar
  10. wil_tid_ampdu_rx_alloc
  11. wil_tid_ampdu_rx_free
  12. wil_agg_size
  13. wil_addba_rx_request
  14. wil_addba_tx_request

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

/* [<][>][^][v][top][bottom][index][help] */