From 37359a82e85dab864b3aee0058f6deacc6448c21 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Thu, 25 Jul 2019 15:37:39 +0200 Subject: [PATCH 3/7] Revert "iwlwifi: mvm: support mac80211 AMSDU" This reverts commit 438af9698b0f161286c6e5d24255c3c231988b39. --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 33 ---- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 - drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c | 14 -- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 14 -- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 229 +++++++++++++++++++++- 5 files changed, 226 insertions(+), 67 deletions(-) Index: src/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c =================================================================== --- src.orig/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c 2019-07-31 18:50:33.492326106 +0200 +++ src/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c 2019-07-31 18:50:40.636230724 +0200 @@ -429,8 +429,6 @@ ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); ieee80211_hw_set(hw, BUFF_MMPDU_TXQ); ieee80211_hw_set(hw, STA_MMPDU_TXQ); - ieee80211_hw_set(hw, TX_AMSDU); - ieee80211_hw_set(hw, TX_FRAG_LIST); if (iwl_mvm_has_tlc_offload(mvm)) { ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); @@ -476,7 +474,6 @@ hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; - hw->max_tx_fragments = mvm->trans->max_skb_frags; BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 6); memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers)); @@ -739,7 +736,6 @@ } hw->netdev_features |= mvm->cfg->features; - hw->netdev_features &= ~(NETIF_F_TSO | NETIF_F_TSO6); if (!iwl_mvm_is_csum_supported(mvm)) { hw->netdev_features &= ~(IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM); @@ -3169,8 +3165,6 @@ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, NL80211_TDLS_SETUP); } - - sta->max_rc_amsdu_len = 1; } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_AUTH) { /* @@ -4918,32 +4912,6 @@ mutex_unlock(&mvm->mutex); } -static bool iwl_mvm_can_hw_csum(struct sk_buff *skb) -{ - u8 protocol = ip_hdr(skb)->protocol; - - if (!IS_ENABLED(CONFIG_INET)) - return false; - - return protocol == IPPROTO_TCP || protocol == IPPROTO_UDP; -} - -static bool iwl_mvm_mac_can_aggregate(struct ieee80211_hw *hw, - struct sk_buff *head, - struct sk_buff *skb) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* For now don't aggregate IPv6 in AMSDU */ - if (skb->protocol != htons(ETH_P_IP)) - return false; - - if (!iwl_mvm_is_csum_supported(mvm)) - return true; - - return iwl_mvm_can_hw_csum(skb) == iwl_mvm_can_hw_csum(head); -} - const struct ieee80211_ops iwl_mvm_hw_ops = { .tx = iwl_mvm_mac_tx, .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, @@ -5022,7 +4990,6 @@ #endif .get_survey = iwl_mvm_mac_get_survey, .sta_statistics = iwl_mvm_mac_sta_statistics, - .can_aggregate_in_amsdu = iwl_mvm_mac_can_aggregate, #ifdef CONFIG_IWLWIFI_DEBUGFS .sta_add_debugfs = iwl_mvm_sta_add_debugfs, #endif Index: src/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h =================================================================== --- src.orig/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h 2019-07-25 15:31:29.301154126 +0200 +++ src/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h 2019-07-31 18:50:40.637230710 +0200 @@ -1553,9 +1553,6 @@ struct ieee80211_tx_info *info, struct ieee80211_sta *sta, __le16 fc); void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq); -unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - unsigned int tid); #ifdef CONFIG_IWLWIFI_DEBUG const char *iwl_mvm_get_tx_fail_reason(u32 status); Index: src/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c =================================================================== --- src.orig/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c 2019-06-27 14:54:04.138678277 +0200 +++ src/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c 2019-07-31 18:50:40.638230697 +0200 @@ -346,26 +346,12 @@ if (flags & IWL_TLC_NOTIF_FLAG_AMSDU) { u16 size = le32_to_cpu(notif->amsdu_size); - int i; if (WARN_ON(sta->max_amsdu_len < size)) goto out; mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled); mvmsta->max_amsdu_len = size; - sta->max_rc_amsdu_len = mvmsta->max_amsdu_len; - - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - if (mvmsta->amsdu_enabled & BIT(i)) - sta->max_tid_amsdu_len[i] = - iwl_mvm_max_amsdu_size(mvm, sta, i); - else - /* - * Not so elegant, but this will effectively - * prevent AMSDU on this TID - */ - sta->max_tid_amsdu_len[i] = 1; - } IWL_DEBUG_RATE(mvm, "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n", Index: src/drivers/net/wireless/intel/iwlwifi/mvm/rs.c =================================================================== --- src.orig/drivers/net/wireless/intel/iwlwifi/mvm/rs.c 2019-07-31 18:50:25.238436309 +0200 +++ src/drivers/net/wireless/intel/iwlwifi/mvm/rs.c 2019-07-31 18:50:40.639230684 +0200 @@ -1751,7 +1751,6 @@ enum rs_action scale_action) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - int i; /* * In case TLC offload is not active amsdu_enabled is either 0xFFFF @@ -1765,19 +1764,6 @@ mvmsta->amsdu_enabled = 0xFFFF; mvmsta->max_amsdu_len = sta->max_amsdu_len; - sta->max_rc_amsdu_len = mvmsta->max_amsdu_len; - - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - if (mvmsta->amsdu_enabled) - sta->max_tid_amsdu_len[i] = - iwl_mvm_max_amsdu_size(mvm, sta, i); - else - /* - * Not so elegant, but this will effectively - * prevent AMSDU on this TID - */ - sta->max_tid_amsdu_len[i] = 1; - } } /* Index: src/drivers/net/wireless/intel/iwlwifi/mvm/tx.c =================================================================== --- src.orig/drivers/net/wireless/intel/iwlwifi/mvm/tx.c 2019-07-31 18:50:33.494326080 +0200 +++ src/drivers/net/wireless/intel/iwlwifi/mvm/tx.c 2019-07-31 18:50:40.639230684 +0200 @@ -785,8 +785,78 @@ return 0; } -unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, unsigned int tid) +#ifdef CONFIG_INET + +static int +iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, + netdev_features_t netdev_flags, + struct sk_buff_head *mpdus_skb) +{ + struct sk_buff *tmp, *next; + struct ieee80211_hdr *hdr = (void *)skb->data; + char cb[sizeof(skb->cb)]; + u16 i = 0; + unsigned int tcp_payload_len; + unsigned int mss = skb_shinfo(skb)->gso_size; + bool ipv4 = (skb->protocol == htons(ETH_P_IP)); + u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; + + skb_shinfo(skb)->gso_size = num_subframes * mss; + memcpy(cb, skb->cb, sizeof(cb)); + + next = skb_gso_segment(skb, netdev_flags); + skb_shinfo(skb)->gso_size = mss; + if (WARN_ON_ONCE(IS_ERR(next))) + return -EINVAL; + else if (next) + consume_skb(skb); + + while (next) { + tmp = next; + next = tmp->next; + + memcpy(tmp->cb, cb, sizeof(tmp->cb)); + /* + * Compute the length of all the data added for the A-MSDU. + * This will be used to compute the length to write in the TX + * command. We have: SNAP + IP + TCP for n -1 subframes and + * ETH header for n subframes. + */ + tcp_payload_len = skb_tail_pointer(tmp) - + skb_transport_header(tmp) - + tcp_hdrlen(tmp) + tmp->data_len; + + if (ipv4) + ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes); + + if (tcp_payload_len > mss) { + skb_shinfo(tmp)->gso_size = mss; + } else { + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc; + + if (ipv4) + ip_send_check(ip_hdr(tmp)); + + qc = ieee80211_get_qos_ctl((void *)tmp->data); + *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + } + skb_shinfo(tmp)->gso_size = 0; + } + + tmp->prev = NULL; + tmp->next = NULL; + + __skb_queue_tail(mpdus_skb, tmp); + i++; + } + + return 0; +} + +static unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + unsigned int tid) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); enum nl80211_band band = mvmsta->vif->bss_conf.chandef.chan->band; @@ -814,6 +884,128 @@ mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); } +static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff_head *mpdus_skb) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct ieee80211_hdr *hdr = (void *)skb->data; + unsigned int mss = skb_shinfo(skb)->gso_size; + unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len; + u16 snap_ip_tcp, pad; + unsigned int dbg_max_amsdu_len; + netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG; + u8 tid; + + snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) + + tcp_hdrlen(skb); + + dbg_max_amsdu_len = READ_ONCE(mvm->max_amsdu_len); + + if (!mvmsta->max_amsdu_len || + !ieee80211_is_data_qos(hdr->frame_control) || + (!mvmsta->amsdu_enabled && !dbg_max_amsdu_len)) + return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); + + /* + * Do not build AMSDU for IPv6 with extension headers. + * ask stack to segment and checkum the generated MPDUs for us. + */ + if (skb->protocol == htons(ETH_P_IPV6) && + ((struct ipv6hdr *)skb_network_header(skb))->nexthdr != + IPPROTO_TCP) { + netdev_flags &= ~NETIF_F_CSUM_MASK; + return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); + } + + tid = ieee80211_get_tid(hdr); + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + return -EINVAL; + + /* + * No need to lock amsdu_in_ampdu_allowed since it can't be modified + * during an BA session. + */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed) + return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); + + if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(mvmsta->vif)) || + !(mvmsta->amsdu_enabled & BIT(tid))) + return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); + + max_amsdu_len = iwl_mvm_max_amsdu_size(mvm, sta, tid); + + if (unlikely(dbg_max_amsdu_len)) + max_amsdu_len = min_t(unsigned int, max_amsdu_len, + dbg_max_amsdu_len); + + /* + * Limit A-MSDU in A-MPDU to 4095 bytes when VHT is not + * supported. This is a spec requirement (IEEE 802.11-2015 + * section 8.7.3 NOTE 3). + */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + !sta->vht_cap.vht_supported) + max_amsdu_len = min_t(unsigned int, max_amsdu_len, 4095); + + /* Sub frame header + SNAP + IP header + TCP header + MSS */ + subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss; + pad = (4 - subf_len) & 0x3; + + /* + * If we have N subframes in the A-MSDU, then the A-MSDU's size is + * N * subf_len + (N - 1) * pad. + */ + num_subframes = (max_amsdu_len + pad) / (subf_len + pad); + + if (sta->max_amsdu_subframes && + num_subframes > sta->max_amsdu_subframes) + num_subframes = sta->max_amsdu_subframes; + + tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - + tcp_hdrlen(skb) + skb->data_len; + + /* + * Make sure we have enough TBs for the A-MSDU: + * 2 for each subframe + * 1 more for each fragment + * 1 more for the potential data in the header + */ + if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) > + mvm->trans->max_skb_frags) + num_subframes = 1; + + if (num_subframes > 1) + *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; + + /* This skb fits in one single A-MSDU */ + if (num_subframes * mss >= tcp_payload_len) { + __skb_queue_tail(mpdus_skb, skb); + return 0; + } + + /* + * Trick the segmentation function to make it + * create SKBs that can fit into one A-MSDU. + */ + return iwl_mvm_tx_tso_segment(skb, num_subframes, netdev_flags, + mpdus_skb); +} +#else /* CONFIG_INET */ +static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff_head *mpdus_skb) +{ + /* Impossible to get TSO with CONFIG_INET */ + WARN_ON(1); + + return -1; +} +#endif + /* Check if there are any timed-out TIDs on a given shared TXQ */ static bool iwl_mvm_txq_should_update(struct iwl_mvm *mvm, int txq_id) { @@ -1012,6 +1204,9 @@ { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct ieee80211_tx_info info; + struct sk_buff_head mpdus_skbs; + unsigned int payload_len; + int ret; if (WARN_ON_ONCE(!mvmsta)) return -1; @@ -1021,7 +1216,35 @@ memcpy(&info, skb->cb, sizeof(info)); - return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); + if (!skb_is_gso(skb)) + return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); + + payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - + tcp_hdrlen(skb) + skb->data_len; + + if (payload_len <= skb_shinfo(skb)->gso_size) + return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); + + __skb_queue_head_init(&mpdus_skbs); + + ret = iwl_mvm_tx_tso(mvm, skb, &info, sta, &mpdus_skbs); + if (ret) + return ret; + + if (WARN_ON(skb_queue_empty(&mpdus_skbs))) + return ret; + + while (!skb_queue_empty(&mpdus_skbs)) { + skb = __skb_dequeue(&mpdus_skbs); + + ret = iwl_mvm_tx_mpdu(mvm, skb, &info, sta); + if (ret) { + __skb_queue_purge(&mpdus_skbs); + return ret; + } + } + + return 0; } static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,