diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2015-10-26 10:00:29 -0400 |
---|---|---|
committer | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2016-02-27 15:00:01 -0500 |
commit | bb81bb68f472bb0f9afbb483259d91d4efd86bfb (patch) | |
tree | 9885a84948a24719c515ccdb27d64ad8a25157b1 /drivers/net/wireless/intel | |
parent | a6d5e32f247cbd3e34c7f86effbf4b426a018c32 (diff) |
iwlwifi: mvm: add Tx A-MSDU inside A-MPDU
If the peer allows, we can have A-MSDU inside A-MDPU.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless/intel')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 37 |
4 files changed, 38 insertions, 12 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index f1ed90bc2740..1a4946fc9b27 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | |||
@@ -847,6 +847,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, | |||
847 | u16 tid = params->tid; | 847 | u16 tid = params->tid; |
848 | u16 *ssn = ¶ms->ssn; | 848 | u16 *ssn = ¶ms->ssn; |
849 | u8 buf_size = params->buf_size; | 849 | u8 buf_size = params->buf_size; |
850 | bool amsdu = params->amsdu; | ||
850 | 851 | ||
851 | IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", | 852 | IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", |
852 | sta->addr, tid, action); | 853 | sta->addr, tid, action); |
@@ -907,7 +908,8 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, | |||
907 | ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid); | 908 | ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid); |
908 | break; | 909 | break; |
909 | case IEEE80211_AMPDU_TX_OPERATIONAL: | 910 | case IEEE80211_AMPDU_TX_OPERATIONAL: |
910 | ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size); | 911 | ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, |
912 | buf_size, amsdu); | ||
911 | break; | 913 | break; |
912 | default: | 914 | default: |
913 | WARN_ON_ONCE(1); | 915 | WARN_ON_ONCE(1); |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 4854e79cbda8..b2123ce3e3a8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c | |||
@@ -1031,7 +1031,8 @@ release_locks: | |||
1031 | } | 1031 | } |
1032 | 1032 | ||
1033 | int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 1033 | int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
1034 | struct ieee80211_sta *sta, u16 tid, u8 buf_size) | 1034 | struct ieee80211_sta *sta, u16 tid, u8 buf_size, |
1035 | bool amsdu) | ||
1035 | { | 1036 | { |
1036 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | 1037 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); |
1037 | struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; | 1038 | struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; |
@@ -1051,6 +1052,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | |||
1051 | tid_data->state = IWL_AGG_ON; | 1052 | tid_data->state = IWL_AGG_ON; |
1052 | mvmsta->agg_tids |= BIT(tid); | 1053 | mvmsta->agg_tids |= BIT(tid); |
1053 | tid_data->ssn = 0xffff; | 1054 | tid_data->ssn = 0xffff; |
1055 | tid_data->amsdu_in_ampdu_allowed = amsdu; | ||
1054 | spin_unlock_bh(&mvmsta->lock); | 1056 | spin_unlock_bh(&mvmsta->lock); |
1055 | 1057 | ||
1056 | fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; | 1058 | fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index e3b9446ee995..b01650ac3598 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h | |||
@@ -260,6 +260,7 @@ enum iwl_mvm_agg_state { | |||
260 | * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). | 260 | * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). |
261 | * @reduced_tpc: Reduced tx power. Holds the data between the | 261 | * @reduced_tpc: Reduced tx power. Holds the data between the |
262 | * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). | 262 | * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). |
263 | * @amsdu_in_ampdu_allowed: true if A-MSDU in A-MPDU is allowed. | ||
263 | * @state: state of the BA agreement establishment / tear down. | 264 | * @state: state of the BA agreement establishment / tear down. |
264 | * @txq_id: Tx queue used by the BA session | 265 | * @txq_id: Tx queue used by the BA session |
265 | * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or | 266 | * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or |
@@ -274,6 +275,7 @@ struct iwl_mvm_tid_data { | |||
274 | /* The rest is Tx AGG related */ | 275 | /* The rest is Tx AGG related */ |
275 | u32 rate_n_flags; | 276 | u32 rate_n_flags; |
276 | u8 reduced_tpc; | 277 | u8 reduced_tpc; |
278 | bool amsdu_in_ampdu_allowed; | ||
277 | enum iwl_mvm_agg_state state; | 279 | enum iwl_mvm_agg_state state; |
278 | u16 txq_id; | 280 | u16 txq_id; |
279 | u16 ssn; | 281 | u16 ssn; |
@@ -405,7 +407,8 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, | |||
405 | int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 407 | int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
406 | struct ieee80211_sta *sta, u16 tid, u16 *ssn); | 408 | struct ieee80211_sta *sta, u16 tid, u16 *ssn); |
407 | int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 409 | int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
408 | struct ieee80211_sta *sta, u16 tid, u8 buf_size); | 410 | struct ieee80211_sta *sta, u16 tid, u8 buf_size, |
411 | bool amsdu); | ||
409 | int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 412 | int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
410 | struct ieee80211_sta *sta, u16 tid); | 413 | struct ieee80211_sta *sta, u16 tid); |
411 | int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 414 | int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 6f67de5a2858..ff08b17b76dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c | |||
@@ -438,19 +438,26 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, | |||
438 | struct ieee80211_sta *sta, | 438 | struct ieee80211_sta *sta, |
439 | struct sk_buff_head *mpdus_skb) | 439 | struct sk_buff_head *mpdus_skb) |
440 | { | 440 | { |
441 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | ||
441 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 442 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
442 | struct ieee80211_hdr *hdr = (void *)skb->data; | 443 | struct ieee80211_hdr *hdr = (void *)skb->data; |
443 | unsigned int mss = skb_shinfo(skb)->gso_size; | 444 | unsigned int mss = skb_shinfo(skb)->gso_size; |
444 | struct sk_buff *tmp, *next; | 445 | struct sk_buff *tmp, *next; |
445 | char cb[sizeof(skb->cb)]; | 446 | char cb[sizeof(skb->cb)]; |
446 | unsigned int num_subframes, tcp_payload_len, subf_len; | 447 | unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len; |
447 | bool ipv4 = (skb->protocol == htons(ETH_P_IP)); | 448 | bool ipv4 = (skb->protocol == htons(ETH_P_IP)); |
448 | u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; | 449 | u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; |
449 | u16 amsdu_add, snap_ip_tcp, pad, i = 0; | 450 | u16 amsdu_add, snap_ip_tcp, pad, i = 0; |
451 | u8 *qc, tid; | ||
450 | 452 | ||
451 | snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) + | 453 | snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) + |
452 | tcp_hdrlen(skb); | 454 | tcp_hdrlen(skb); |
453 | 455 | ||
456 | qc = ieee80211_get_qos_ctl(hdr); | ||
457 | tid = *qc & IEEE80211_QOS_CTL_TID_MASK; | ||
458 | if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) | ||
459 | return -EINVAL; | ||
460 | |||
454 | if (!sta->max_amsdu_len || | 461 | if (!sta->max_amsdu_len || |
455 | !ieee80211_is_data_qos(hdr->frame_control)) { | 462 | !ieee80211_is_data_qos(hdr->frame_control)) { |
456 | num_subframes = 1; | 463 | num_subframes = 1; |
@@ -458,13 +465,28 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, | |||
458 | goto segment; | 465 | goto segment; |
459 | } | 466 | } |
460 | 467 | ||
461 | /* TODO: for now, disable A-MSDU inside AMPDU */ | 468 | /* |
462 | if (info->flags & IEEE80211_TX_CTL_AMPDU) { | 469 | * No need to lock amsdu_in_ampdu_allowed since it can't be modified |
470 | * during an BA session. | ||
471 | */ | ||
472 | if (info->flags & IEEE80211_TX_CTL_AMPDU && | ||
473 | !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed) { | ||
463 | num_subframes = 1; | 474 | num_subframes = 1; |
464 | pad = 0; | 475 | pad = 0; |
465 | goto segment; | 476 | goto segment; |
466 | } | 477 | } |
467 | 478 | ||
479 | max_amsdu_len = sta->max_amsdu_len; | ||
480 | |||
481 | /* | ||
482 | * Limit A-MSDU in A-MPDU to 4095 bytes when VHT is not | ||
483 | * supported. This is a spec requirement (IEEE 802.11-2015 | ||
484 | * section 8.7.3 NOTE 3). | ||
485 | */ | ||
486 | if (info->flags & IEEE80211_TX_CTL_AMPDU && | ||
487 | !sta->vht_cap.vht_supported) | ||
488 | max_amsdu_len = min_t(unsigned int, max_amsdu_len, 4095); | ||
489 | |||
468 | /* Sub frame header + SNAP + IP header + TCP header + MSS */ | 490 | /* Sub frame header + SNAP + IP header + TCP header + MSS */ |
469 | subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss; | 491 | subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss; |
470 | pad = (4 - subf_len) & 0x3; | 492 | pad = (4 - subf_len) & 0x3; |
@@ -473,12 +495,9 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, | |||
473 | * If we have N subframes in the A-MSDU, then the A-MSDU's size is | 495 | * If we have N subframes in the A-MSDU, then the A-MSDU's size is |
474 | * N * subf_len + (N - 1) * pad. | 496 | * N * subf_len + (N - 1) * pad. |
475 | */ | 497 | */ |
476 | num_subframes = (sta->max_amsdu_len + pad) / (subf_len + pad); | 498 | num_subframes = (max_amsdu_len + pad) / (subf_len + pad); |
477 | if (num_subframes > 1) { | 499 | if (num_subframes > 1) |
478 | u8 *qc = ieee80211_get_qos_ctl((void *)skb->data); | ||
479 | |||
480 | *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; | 500 | *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; |
481 | } | ||
482 | 501 | ||
483 | tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - | 502 | tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - |
484 | tcp_hdrlen(skb) + skb->data_len; | 503 | tcp_hdrlen(skb) + skb->data_len; |
@@ -555,7 +574,7 @@ segment: | |||
555 | info->driver_data[0] = (void *)(uintptr_t)amsdu_add; | 574 | info->driver_data[0] = (void *)(uintptr_t)amsdu_add; |
556 | skb_shinfo(tmp)->gso_size = mss; | 575 | skb_shinfo(tmp)->gso_size = mss; |
557 | } else { | 576 | } else { |
558 | u8 *qc = ieee80211_get_qos_ctl((void *)tmp->data); | 577 | qc = ieee80211_get_qos_ctl((void *)tmp->data); |
559 | 578 | ||
560 | if (ipv4) | 579 | if (ipv4) |
561 | ip_send_check(ip_hdr(tmp)); | 580 | ip_send_check(ip_hdr(tmp)); |