@@ -808,6 +808,197 @@ unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm,
808
808
mvm -> fwrt .smem_cfg .lmac [lmac ].txfifo_size [txf ] - 256 );
809
809
}
810
810
811
+ #ifdef CONFIG_INET
812
+
813
+ static int
814
+ iwl_mvm_tx_tso_segment (struct sk_buff * skb , unsigned int num_subframes ,
815
+ netdev_features_t netdev_flags ,
816
+ struct sk_buff_head * mpdus_skb )
817
+ {
818
+ struct sk_buff * tmp , * next ;
819
+ struct ieee80211_hdr * hdr = (void * )skb -> data ;
820
+ char cb [sizeof (skb -> cb )];
821
+ u16 i = 0 ;
822
+ unsigned int tcp_payload_len ;
823
+ unsigned int mss = skb_shinfo (skb )-> gso_size ;
824
+ bool ipv4 = (skb -> protocol == htons (ETH_P_IP ));
825
+ u16 ip_base_id = ipv4 ? ntohs (ip_hdr (skb )-> id ) : 0 ;
826
+
827
+ skb_shinfo (skb )-> gso_size = num_subframes * mss ;
828
+ memcpy (cb , skb -> cb , sizeof (cb ));
829
+
830
+ next = skb_gso_segment (skb , netdev_flags );
831
+ skb_shinfo (skb )-> gso_size = mss ;
832
+ if (WARN_ON_ONCE (IS_ERR (next )))
833
+ return - EINVAL ;
834
+ else if (next )
835
+ consume_skb (skb );
836
+
837
+ while (next ) {
838
+ tmp = next ;
839
+ next = tmp -> next ;
840
+
841
+ memcpy (tmp -> cb , cb , sizeof (tmp -> cb ));
842
+ /*
843
+ * Compute the length of all the data added for the A-MSDU.
844
+ * This will be used to compute the length to write in the TX
845
+ * command. We have: SNAP + IP + TCP for n -1 subframes and
846
+ * ETH header for n subframes.
847
+ */
848
+ tcp_payload_len = skb_tail_pointer (tmp ) -
849
+ skb_transport_header (tmp ) -
850
+ tcp_hdrlen (tmp ) + tmp -> data_len ;
851
+
852
+ if (ipv4 )
853
+ ip_hdr (tmp )-> id = htons (ip_base_id + i * num_subframes );
854
+
855
+ if (tcp_payload_len > mss ) {
856
+ skb_shinfo (tmp )-> gso_size = mss ;
857
+ } else {
858
+ if (ieee80211_is_data_qos (hdr -> frame_control )) {
859
+ u8 * qc ;
860
+
861
+ if (ipv4 )
862
+ ip_send_check (ip_hdr (tmp ));
863
+
864
+ qc = ieee80211_get_qos_ctl ((void * )tmp -> data );
865
+ * qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT ;
866
+ }
867
+ skb_shinfo (tmp )-> gso_size = 0 ;
868
+ }
869
+
870
+ tmp -> prev = NULL ;
871
+ tmp -> next = NULL ;
872
+
873
+ __skb_queue_tail (mpdus_skb , tmp );
874
+ i ++ ;
875
+ }
876
+
877
+ return 0 ;
878
+ }
879
+
880
+ static int iwl_mvm_tx_tso (struct iwl_mvm * mvm , struct sk_buff * skb ,
881
+ struct ieee80211_tx_info * info ,
882
+ struct ieee80211_sta * sta ,
883
+ struct sk_buff_head * mpdus_skb )
884
+ {
885
+ struct iwl_mvm_sta * mvmsta = iwl_mvm_sta_from_mac80211 (sta );
886
+ struct ieee80211_hdr * hdr = (void * )skb -> data ;
887
+ unsigned int mss = skb_shinfo (skb )-> gso_size ;
888
+ unsigned int num_subframes , tcp_payload_len , subf_len , max_amsdu_len ;
889
+ u16 snap_ip_tcp , pad ;
890
+ unsigned int dbg_max_amsdu_len ;
891
+ netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG ;
892
+ u8 tid ;
893
+
894
+ snap_ip_tcp = 8 + skb_transport_header (skb ) - skb_network_header (skb ) +
895
+ tcp_hdrlen (skb );
896
+
897
+ dbg_max_amsdu_len = READ_ONCE (mvm -> max_amsdu_len );
898
+
899
+ if (!mvmsta -> max_amsdu_len ||
900
+ !ieee80211_is_data_qos (hdr -> frame_control ) ||
901
+ (!mvmsta -> amsdu_enabled && !dbg_max_amsdu_len ))
902
+ return iwl_mvm_tx_tso_segment (skb , 1 , netdev_flags , mpdus_skb );
903
+
904
+ /*
905
+ * Do not build AMSDU for IPv6 with extension headers.
906
+ * ask stack to segment and checkum the generated MPDUs for us.
907
+ */
908
+ if (skb -> protocol == htons (ETH_P_IPV6 ) &&
909
+ ((struct ipv6hdr * )skb_network_header (skb ))-> nexthdr !=
910
+ IPPROTO_TCP ) {
911
+ netdev_flags &= ~NETIF_F_CSUM_MASK ;
912
+ return iwl_mvm_tx_tso_segment (skb , 1 , netdev_flags , mpdus_skb );
913
+ }
914
+
915
+ tid = ieee80211_get_tid (hdr );
916
+ if (WARN_ON_ONCE (tid >= IWL_MAX_TID_COUNT ))
917
+ return - EINVAL ;
918
+
919
+ /*
920
+ * No need to lock amsdu_in_ampdu_allowed since it can't be modified
921
+ * during an BA session.
922
+ */
923
+ if (info -> flags & IEEE80211_TX_CTL_AMPDU &&
924
+ !mvmsta -> tid_data [tid ].amsdu_in_ampdu_allowed )
925
+ return iwl_mvm_tx_tso_segment (skb , 1 , netdev_flags , mpdus_skb );
926
+
927
+ if (iwl_mvm_vif_low_latency (iwl_mvm_vif_from_mac80211 (mvmsta -> vif )) ||
928
+ !(mvmsta -> amsdu_enabled & BIT (tid )))
929
+ return iwl_mvm_tx_tso_segment (skb , 1 , netdev_flags , mpdus_skb );
930
+
931
+ max_amsdu_len = iwl_mvm_max_amsdu_size (mvm , sta , tid );
932
+
933
+ if (unlikely (dbg_max_amsdu_len ))
934
+ max_amsdu_len = min_t (unsigned int , max_amsdu_len ,
935
+ dbg_max_amsdu_len );
936
+
937
+ /*
938
+ * Limit A-MSDU in A-MPDU to 4095 bytes when VHT is not
939
+ * supported. This is a spec requirement (IEEE 802.11-2015
940
+ * section 8.7.3 NOTE 3).
941
+ */
942
+ if (info -> flags & IEEE80211_TX_CTL_AMPDU &&
943
+ !sta -> vht_cap .vht_supported )
944
+ max_amsdu_len = min_t (unsigned int , max_amsdu_len , 4095 );
945
+
946
+ /* Sub frame header + SNAP + IP header + TCP header + MSS */
947
+ subf_len = sizeof (struct ethhdr ) + snap_ip_tcp + mss ;
948
+ pad = (4 - subf_len ) & 0x3 ;
949
+
950
+ /*
951
+ * If we have N subframes in the A-MSDU, then the A-MSDU's size is
952
+ * N * subf_len + (N - 1) * pad.
953
+ */
954
+ num_subframes = (max_amsdu_len + pad ) / (subf_len + pad );
955
+
956
+ if (sta -> max_amsdu_subframes &&
957
+ num_subframes > sta -> max_amsdu_subframes )
958
+ num_subframes = sta -> max_amsdu_subframes ;
959
+
960
+ tcp_payload_len = skb_tail_pointer (skb ) - skb_transport_header (skb ) -
961
+ tcp_hdrlen (skb ) + skb -> data_len ;
962
+
963
+ /*
964
+ * Make sure we have enough TBs for the A-MSDU:
965
+ * 2 for each subframe
966
+ * 1 more for each fragment
967
+ * 1 more for the potential data in the header
968
+ */
969
+ if ((num_subframes * 2 + skb_shinfo (skb )-> nr_frags + 1 ) >
970
+ mvm -> trans -> max_skb_frags )
971
+ num_subframes = 1 ;
972
+
973
+ if (num_subframes > 1 )
974
+ * ieee80211_get_qos_ctl (hdr ) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT ;
975
+
976
+ /* This skb fits in one single A-MSDU */
977
+ if (num_subframes * mss >= tcp_payload_len ) {
978
+ __skb_queue_tail (mpdus_skb , skb );
979
+ return 0 ;
980
+ }
981
+
982
+ /*
983
+ * Trick the segmentation function to make it
984
+ * create SKBs that can fit into one A-MSDU.
985
+ */
986
+ return iwl_mvm_tx_tso_segment (skb , num_subframes , netdev_flags ,
987
+ mpdus_skb );
988
+ }
989
+ #else /* CONFIG_INET */
990
+ static int iwl_mvm_tx_tso (struct iwl_mvm * mvm , struct sk_buff * skb ,
991
+ struct ieee80211_tx_info * info ,
992
+ struct ieee80211_sta * sta ,
993
+ struct sk_buff_head * mpdus_skb )
994
+ {
995
+ /* Impossible to get TSO with CONFIG_INET */
996
+ WARN_ON (1 );
997
+
998
+ return -1 ;
999
+ }
1000
+ #endif
1001
+
811
1002
/* Check if there are any timed-out TIDs on a given shared TXQ */
812
1003
static bool iwl_mvm_txq_should_update (struct iwl_mvm * mvm , int txq_id )
813
1004
{
@@ -986,6 +1177,9 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
986
1177
{
987
1178
struct iwl_mvm_sta * mvmsta = iwl_mvm_sta_from_mac80211 (sta );
988
1179
struct ieee80211_tx_info info ;
1180
+ struct sk_buff_head mpdus_skbs ;
1181
+ unsigned int payload_len ;
1182
+ int ret ;
989
1183
990
1184
if (WARN_ON_ONCE (!mvmsta ))
991
1185
return -1 ;
@@ -995,7 +1189,35 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
995
1189
996
1190
memcpy (& info , skb -> cb , sizeof (info ));
997
1191
998
- return iwl_mvm_tx_mpdu (mvm , skb , & info , sta );
1192
+ if (!skb_is_gso (skb ))
1193
+ return iwl_mvm_tx_mpdu (mvm , skb , & info , sta );
1194
+
1195
+ payload_len = skb_tail_pointer (skb ) - skb_transport_header (skb ) -
1196
+ tcp_hdrlen (skb ) + skb -> data_len ;
1197
+
1198
+ if (payload_len <= skb_shinfo (skb )-> gso_size )
1199
+ return iwl_mvm_tx_mpdu (mvm , skb , & info , sta );
1200
+
1201
+ __skb_queue_head_init (& mpdus_skbs );
1202
+
1203
+ ret = iwl_mvm_tx_tso (mvm , skb , & info , sta , & mpdus_skbs );
1204
+ if (ret )
1205
+ return ret ;
1206
+
1207
+ if (WARN_ON (skb_queue_empty (& mpdus_skbs )))
1208
+ return ret ;
1209
+
1210
+ while (!skb_queue_empty (& mpdus_skbs )) {
1211
+ skb = __skb_dequeue (& mpdus_skbs );
1212
+
1213
+ ret = iwl_mvm_tx_mpdu (mvm , skb , & info , sta );
1214
+ if (ret ) {
1215
+ __skb_queue_purge (& mpdus_skbs );
1216
+ return ret ;
1217
+ }
1218
+ }
1219
+
1220
+ return 0 ;
999
1221
}
1000
1222
1001
1223
static void iwl_mvm_check_ratid_empty (struct iwl_mvm * mvm ,
0 commit comments