ath10k: remove htt rx amsdu clear retry bit hack
[cascardo/linux.git] / drivers / net / wireless / ath / ath10k / htt_rx.c
index e784c40..0335218 100644 (file)
@@ -20,6 +20,7 @@
 #include "htt.h"
 #include "txrx.h"
 #include "debug.h"
+#include "trace.h"
 
 #include <linux/log2.h>
 
 /* when under memory pressure rx ring refill may fail and needs a retry */
 #define HTT_RX_RING_REFILL_RETRY_MS 50
 
+
+static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
+
+
 static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
 {
        int size;
@@ -177,10 +182,27 @@ static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
 
 static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
 {
-       int ret, num_to_fill;
+       int ret, num_deficit, num_to_fill;
 
+       /* Refilling the whole RX ring buffer proves to be a bad idea. The
+        * reason is RX may take up significant amount of CPU cycles and starve
+        * other tasks, e.g. TX on an ethernet device while acting as a bridge
+        * with ath10k wlan interface. This ended up with very poor performance
+        * once CPU the host system was overwhelmed with RX on ath10k.
+        *
+        * By limiting the number of refills the replenishing occurs
+        * progressively. This in turns makes use of the fact tasklets are
+        * processed in FIFO order. This means actual RX processing can starve
+        * out refilling. If there's not enough buffers on RX ring FW will not
+        * report RX until it is refilled with enough buffers. This
+        * automatically balances load wrt to CPU power.
+        *
+        * This probably comes at a cost of lower maximum throughput but
+        * improves the avarage and stability. */
        spin_lock_bh(&htt->rx_ring.lock);
-       num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+       num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+       num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit);
+       num_deficit -= num_to_fill;
        ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
        if (ret == -ENOMEM) {
                /*
@@ -191,6 +213,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
                 */
                mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
                          msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
+       } else if (num_deficit > 0) {
+               tasklet_schedule(&htt->rx_replenish_task);
        }
        spin_unlock_bh(&htt->rx_ring.lock);
 }
@@ -212,6 +236,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt)
        int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
 
        del_timer_sync(&htt->rx_ring.refill_retry_timer);
+       tasklet_kill(&htt->rx_replenish_task);
 
        while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
                struct sk_buff *skb =
@@ -441,6 +466,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        return msdu_chaining;
 }
 
+static void ath10k_htt_rx_replenish_task(unsigned long ptr)
+{
+       struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
+       ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
 int ath10k_htt_rx_attach(struct ath10k_htt *htt)
 {
        dma_addr_t paddr;
@@ -501,7 +532,10 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt)
        if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
                goto err_fill_ring;
 
-       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n",
+       tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
+                    (unsigned long)htt);
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
                   htt->rx_ring.size, htt->rx_ring.fill_level);
        return 0;
 
@@ -590,134 +624,130 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
        return false;
 }
 
-static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
-                       struct htt_rx_info *info)
+struct rfc1042_hdr {
+       u8 llc_dsap;
+       u8 llc_ssap;
+       u8 llc_ctrl;
+       u8 snap_oui[3];
+       __be16 snap_type;
+} __packed;
+
+struct amsdu_subframe_hdr {
+       u8 dst[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       __be16 len;
+} __packed;
+
+static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
+                               struct htt_rx_info *info)
 {
        struct htt_rx_desc *rxd;
-       struct sk_buff *amsdu;
        struct sk_buff *first;
-       struct ieee80211_hdr *hdr;
        struct sk_buff *skb = info->skb;
        enum rx_msdu_decap_format fmt;
        enum htt_rx_mpdu_encrypt_type enctype;
+       struct ieee80211_hdr *hdr;
+       u8 hdr_buf[64], addr[ETH_ALEN], *qos;
        unsigned int hdr_len;
-       int crypto_len;
 
        rxd = (void *)skb->data - sizeof(*rxd);
-       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-                       RX_MSDU_START_INFO1_DECAP_FORMAT);
        enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
                        RX_MPDU_START_INFO0_ENCRYPT_TYPE);
 
-       /* FIXME: No idea what assumptions are safe here. Need logs */
-       if ((fmt == RX_MSDU_DECAP_RAW && skb->next) ||
-           (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) {
-               ath10k_htt_rx_free_msdu_chain(skb->next);
-               skb->next = NULL;
-               return -ENOTSUPP;
-       }
-
-       /* A-MSDU max is a little less than 8K */
-       amsdu = dev_alloc_skb(8*1024);
-       if (!amsdu) {
-               ath10k_warn("A-MSDU allocation failed\n");
-               ath10k_htt_rx_free_msdu_chain(skb->next);
-               skb->next = NULL;
-               return -ENOMEM;
-       }
-
-       if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
-               int hdrlen;
-
-               hdr = (void *)rxd->rx_hdr_status;
-               hdrlen = ieee80211_hdrlen(hdr->frame_control);
-               memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
-       }
+       hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+       memcpy(hdr_buf, hdr, hdr_len);
+       hdr = (struct ieee80211_hdr *)hdr_buf;
 
        first = skb;
        while (skb) {
                void *decap_hdr;
-               int decap_len = 0;
+               int len;
 
                rxd = (void *)skb->data - sizeof(*rxd);
                fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-                               RX_MSDU_START_INFO1_DECAP_FORMAT);
+                        RX_MSDU_START_INFO1_DECAP_FORMAT);
                decap_hdr = (void *)rxd->rx_hdr_status;
 
-               if (skb == first) {
-                       /* We receive linked A-MSDU subframe skbuffs. The
-                        * first one contains the original 802.11 header (and
-                        * possible crypto param) in the RX descriptor. The
-                        * A-MSDU subframe header follows that. Each part is
-                        * aligned to 4 byte boundary. */
+               skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
 
-                       hdr = (void *)amsdu->data;
-                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
-                       crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
-
-                       decap_hdr += roundup(hdr_len, 4);
-                       decap_hdr += roundup(crypto_len, 4);
-               }
-
-               if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
-                       /* Ethernet2 decap inserts ethernet header in place of
-                        * A-MSDU subframe header. */
-                       skb_pull(skb, 6 + 6 + 2);
-
-                       /* A-MSDU subframe header length */
-                       decap_len += 6 + 6 + 2;
-
-                       /* Ethernet2 decap also strips the LLC/SNAP so we need
-                        * to re-insert it. The LLC/SNAP follows A-MSDU
-                        * subframe header. */
-                       /* FIXME: Not all LLCs are 8 bytes long */
-                       decap_len += 8;
-
-                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+               /* First frame in an A-MSDU chain has more decapped data. */
+               if (skb == first) {
+                       len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
+                       len += round_up(ath10k_htt_rx_crypto_param_len(enctype),
+                                       4);
+                       decap_hdr += len;
                }
 
-               if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) {
-                       /* Native Wifi decap inserts regular 802.11 header
-                        * in place of A-MSDU subframe header. */
+               switch (fmt) {
+               case RX_MSDU_DECAP_RAW:
+                       /* remove trailing FCS */
+                       skb_trim(skb, skb->len - FCS_LEN);
+                       break;
+               case RX_MSDU_DECAP_NATIVE_WIFI:
+                       /* pull decapped header and copy DA */
                        hdr = (struct ieee80211_hdr *)skb->data;
-                       skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
+                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+                       memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN);
+                       skb_pull(skb, hdr_len);
 
-                       /* A-MSDU subframe header length */
-                       decap_len += 6 + 6 + 2;
+                       /* push original 802.11 header */
+                       hdr = (struct ieee80211_hdr *)hdr_buf;
+                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
 
-                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
-               }
+                       /* original A-MSDU header has the bit set but we're
+                        * not including A-MSDU subframe header */
+                       hdr = (struct ieee80211_hdr *)skb->data;
+                       qos = ieee80211_get_qos_ctl(hdr);
+                       qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
 
-               if (fmt == RX_MSDU_DECAP_RAW)
-                       skb_trim(skb, skb->len - 4); /* remove FCS */
+                       /* original 802.11 header has a different DA */
+                       memcpy(ieee80211_get_DA(hdr), addr, ETH_ALEN);
+                       break;
+               case RX_MSDU_DECAP_ETHERNET2_DIX:
+                       /* strip ethernet header and insert decapped 802.11
+                        * header, amsdu subframe header and rfc1042 header */
 
-               memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
+                       len = 0;
+                       len += sizeof(struct rfc1042_hdr);
+                       len += sizeof(struct amsdu_subframe_hdr);
 
-               /* A-MSDU subframes are padded to 4bytes
-                * but relative to first subframe, not the whole MPDU */
-               if (skb->next && ((decap_len + skb->len) & 3)) {
-                       int padlen = 4 - ((decap_len + skb->len) & 3);
-                       memset(skb_put(amsdu, padlen), 0, padlen);
+                       skb_pull(skb, sizeof(struct ethhdr));
+                       memcpy(skb_push(skb, len), decap_hdr, len);
+                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+                       break;
+               case RX_MSDU_DECAP_8023_SNAP_LLC:
+                       /* insert decapped 802.11 header making a singly
+                        * A-MSDU */
+                       memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+                       break;
                }
 
+               info->skb = skb;
+               info->encrypt_type = enctype;
                skb = skb->next;
-       }
+               info->skb->next = NULL;
 
-       info->skb = amsdu;
-       info->encrypt_type = enctype;
+               if (skb)
+                       info->amsdu_more = true;
 
-       ath10k_htt_rx_free_msdu_chain(first);
+               ath10k_process_rx(htt->ar, info);
+       }
 
-       return 0;
+       /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
+        * monitor interface active for sniffing purposes. */
 }
 
-static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
 {
        struct sk_buff *skb = info->skb;
        struct htt_rx_desc *rxd;
        struct ieee80211_hdr *hdr;
        enum rx_msdu_decap_format fmt;
        enum htt_rx_mpdu_encrypt_type enctype;
+       int hdr_len;
+       void *rfc1042;
 
        /* This shouldn't happen. If it does than it may be a FW bug. */
        if (skb->next) {
@@ -731,49 +761,53 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
                        RX_MSDU_START_INFO1_DECAP_FORMAT);
        enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
                        RX_MPDU_START_INFO0_ENCRYPT_TYPE);
-       hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+       hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+       skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
 
        switch (fmt) {
        case RX_MSDU_DECAP_RAW:
                /* remove trailing FCS */
-               skb_trim(skb, skb->len - 4);
+               skb_trim(skb, skb->len - FCS_LEN);
                break;
        case RX_MSDU_DECAP_NATIVE_WIFI:
-               /* nothing to do here */
+               /* Pull decapped header */
+               hdr = (struct ieee80211_hdr *)skb->data;
+               hdr_len = ieee80211_hdrlen(hdr->frame_control);
+               skb_pull(skb, hdr_len);
+
+               /* Push original header */
+               hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+               hdr_len = ieee80211_hdrlen(hdr->frame_control);
+               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
                break;
        case RX_MSDU_DECAP_ETHERNET2_DIX:
-               /* macaddr[6] + macaddr[6] + ethertype[2] */
-               skb_pull(skb, 6 + 6 + 2);
-               break;
-       case RX_MSDU_DECAP_8023_SNAP_LLC:
-               /* macaddr[6] + macaddr[6] + len[2] */
-               /* we don't need this for non-A-MSDU */
-               skb_pull(skb, 6 + 6 + 2);
-               break;
-       }
-
-       if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
-               void *llc;
-               int llclen;
+               /* strip ethernet header and insert decapped 802.11 header and
+                * rfc1042 header */
 
-               llclen = 8;
-               llc  = hdr;
-               llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4);
-               llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
+               rfc1042 = hdr;
+               rfc1042 += roundup(hdr_len, 4);
+               rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
 
-               skb_push(skb, llclen);
-               memcpy(skb->data, llc, llclen);
-       }
+               skb_pull(skb, sizeof(struct ethhdr));
+               memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
+                      rfc1042, sizeof(struct rfc1042_hdr));
+               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+               break;
+       case RX_MSDU_DECAP_8023_SNAP_LLC:
+               /* remove A-MSDU subframe header and insert
+                * decapped 802.11 header. rfc1042 header is already there */
 
-       if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) {
-               int len = ieee80211_hdrlen(hdr->frame_control);
-               skb_push(skb, len);
-               memcpy(skb->data, hdr, len);
+               skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
+               memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+               break;
        }
 
        info->skb = skb;
        info->encrypt_type = enctype;
-       return 0;
+
+       ath10k_process_rx(htt->ar, info);
 }
 
 static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
@@ -845,8 +879,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
        int fw_desc_len;
        u8 *fw_desc;
        int i, j;
-       int ret;
-       int ip_summed;
 
        memset(&info, 0, sizeof(info));
 
@@ -921,11 +953,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                                continue;
                        }
 
-                       /* The skb is not yet processed and it may be
-                        * reallocated. Since the offload is in the original
-                        * skb extract the checksum now and assign it later */
-                       ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
-
                        info.skb     = msdu_head;
                        info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
                        info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
@@ -938,28 +965,13 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                        hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
 
                        if (ath10k_htt_rx_hdr_is_amsdu(hdr))
-                               ret = ath10k_htt_rx_amsdu(htt, &info);
+                               ath10k_htt_rx_amsdu(htt, &info);
                        else
-                               ret = ath10k_htt_rx_msdu(htt, &info);
-
-                       if (ret && !info.fcs_err) {
-                               ath10k_warn("error processing msdus %d\n", ret);
-                               dev_kfree_skb_any(info.skb);
-                               continue;
-                       }
-
-                       if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
-                               ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
-
-                       info.skb->ip_summed = ip_summed;
-
-                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
-                                       info.skb->data, info.skb->len);
-                       ath10k_process_rx(htt->ar, &info);
+                               ath10k_htt_rx_msdu(htt, &info);
                }
        }
 
-       ath10k_htt_rx_msdu_buff_replenish(htt);
+       tasklet_schedule(&htt->rx_replenish_task);
 }
 
 static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
@@ -1131,7 +1143,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
                        break;
                }
 
-               ath10k_txrx_tx_completed(htt, &tx_done);
+               ath10k_txrx_tx_unref(htt, &tx_done);
                break;
        }
        case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
@@ -1165,7 +1177,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
                for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
                        msdu_id = resp->data_tx_completion.msdus[i];
                        tx_done.msdu_id = __le16_to_cpu(msdu_id);
-                       ath10k_txrx_tx_completed(htt, &tx_done);
+                       ath10k_txrx_tx_unref(htt, &tx_done);
                }
                break;
        }
@@ -1190,8 +1202,10 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
        case HTT_T2H_MSG_TYPE_TEST:
                /* FIX THIS */
                break;
-       case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
        case HTT_T2H_MSG_TYPE_STATS_CONF:
+               trace_ath10k_htt_stats(skb->data, skb->len);
+               break;
+       case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
        case HTT_T2H_MSG_TYPE_RX_ADDBA:
        case HTT_T2H_MSG_TYPE_RX_DELBA:
        case HTT_T2H_MSG_TYPE_RX_FLUSH: