67
67
#include <linux/etherdevice.h>
68
68
#include <linux/tcp.h>
69
69
#include <net/ip.h>
70
+ #include <net/ipv6.h>
70
71
71
72
#include "iwl-trans.h"
72
73
#include "iwl-eeprom-parse.h"
@@ -98,6 +99,111 @@ iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
98
99
addr , tid , ssn );
99
100
}
100
101
102
+ #define OPT_HDR (type , skb , off ) \
103
+ (type *)(skb_network_header(skb) + (off))
104
+
105
+ static void iwl_mvm_tx_csum (struct iwl_mvm * mvm , struct sk_buff * skb ,
106
+ struct ieee80211_hdr * hdr ,
107
+ struct ieee80211_tx_info * info ,
108
+ struct iwl_tx_cmd * tx_cmd )
109
+ {
110
+ #if IS_ENABLED (CONFIG_INET )
111
+ u16 mh_len = ieee80211_hdrlen (hdr -> frame_control );
112
+ u16 offload_assist = le16_to_cpu (tx_cmd -> offload_assist );
113
+ u8 protocol = 0 ;
114
+
115
+ /*
116
+ * Do not compute checksum if already computed or if transport will
117
+ * compute it
118
+ */
119
+ if (skb -> ip_summed != CHECKSUM_PARTIAL || IWL_MVM_SW_TX_CSUM_OFFLOAD )
120
+ return ;
121
+
122
+ /* We do not expect to be requested to csum stuff we do not support */
123
+ if (WARN_ONCE (!(mvm -> hw -> netdev_features & IWL_TX_CSUM_NETIF_FLAGS ) ||
124
+ (skb -> protocol != htons (ETH_P_IP ) &&
125
+ skb -> protocol != htons (ETH_P_IPV6 )),
126
+ "No support for requested checksum\n" )) {
127
+ skb_checksum_help (skb );
128
+ return ;
129
+ }
130
+
131
+ if (skb -> protocol == htons (ETH_P_IP )) {
132
+ protocol = ip_hdr (skb )-> protocol ;
133
+ } else {
134
+ #if IS_ENABLED (CONFIG_IPV6 )
135
+ struct ipv6hdr * ipv6h =
136
+ (struct ipv6hdr * )skb_network_header (skb );
137
+ unsigned int off = sizeof (* ipv6h );
138
+
139
+ protocol = ipv6h -> nexthdr ;
140
+ while (protocol != NEXTHDR_NONE && ipv6_ext_hdr (protocol )) {
141
+ /* only supported extension headers */
142
+ if (protocol != NEXTHDR_ROUTING &&
143
+ protocol != NEXTHDR_HOP &&
144
+ protocol != NEXTHDR_DEST &&
145
+ protocol != NEXTHDR_FRAGMENT ) {
146
+ skb_checksum_help (skb );
147
+ return ;
148
+ }
149
+
150
+ if (protocol == NEXTHDR_FRAGMENT ) {
151
+ struct frag_hdr * hp =
152
+ OPT_HDR (struct frag_hdr , skb , off );
153
+
154
+ protocol = hp -> nexthdr ;
155
+ off += sizeof (struct frag_hdr );
156
+ } else {
157
+ struct ipv6_opt_hdr * hp =
158
+ OPT_HDR (struct ipv6_opt_hdr , skb , off );
159
+
160
+ protocol = hp -> nexthdr ;
161
+ off += ipv6_optlen (hp );
162
+ }
163
+ }
164
+ /* if we get here - protocol now should be TCP/UDP */
165
+ #endif
166
+ }
167
+
168
+ if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP ) {
169
+ WARN_ON_ONCE (1 );
170
+ skb_checksum_help (skb );
171
+ return ;
172
+ }
173
+
174
+ /* enable L4 csum */
175
+ offload_assist |= BIT (TX_CMD_OFFLD_L4_EN );
176
+
177
+ /*
178
+ * Set offset to IP header (snap).
179
+ * We don't support tunneling so no need to take care of inner header.
180
+ * Size is in words.
181
+ */
182
+ offload_assist |= (4 << TX_CMD_OFFLD_IP_HDR );
183
+
184
+ /* Do IPv4 csum for AMSDU only (no IP csum for Ipv6) */
185
+ if (skb -> protocol == htons (ETH_P_IP ) &&
186
+ (offload_assist & BIT (TX_CMD_OFFLD_AMSDU ))) {
187
+ ip_hdr (skb )-> check = 0 ;
188
+ offload_assist |= BIT (TX_CMD_OFFLD_L3_EN );
189
+ }
190
+
191
+ /* reset UDP/TCP header csum */
192
+ if (protocol == IPPROTO_TCP )
193
+ tcp_hdr (skb )-> check = 0 ;
194
+ else
195
+ udp_hdr (skb )-> check = 0 ;
196
+
197
+ /* mac header len should include IV, size is in words */
198
+ if (info -> control .hw_key )
199
+ mh_len += info -> control .hw_key -> iv_len ;
200
+ mh_len /= 2 ;
201
+ offload_assist |= mh_len << TX_CMD_OFFLD_MH_SIZE ;
202
+
203
+ tx_cmd -> offload_assist = cpu_to_le16 (offload_assist );
204
+ #endif
205
+ }
206
+
101
207
/*
102
208
* Sets most of the Tx cmd's fields
103
209
*/
@@ -196,6 +302,8 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
196
302
if (ieee80211_hdrlen (fc ) % 4 &&
197
303
!(tx_cmd -> offload_assist & cpu_to_le16 (BIT (TX_CMD_OFFLD_AMSDU ))))
198
304
tx_cmd -> offload_assist |= cpu_to_le16 (BIT (TX_CMD_OFFLD_PAD ));
305
+
306
+ iwl_mvm_tx_csum (mvm , skb , hdr , info , tx_cmd );
199
307
}
200
308
201
309
/*
@@ -466,6 +574,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
466
574
u16 ip_base_id = ipv4 ? ntohs (ip_hdr (skb )-> id ) : 0 ;
467
575
u16 amsdu_add , snap_ip_tcp , pad , i = 0 ;
468
576
unsigned int dbg_max_amsdu_len ;
577
+ netdev_features_t netdev_features = NETIF_F_CSUM_MASK | NETIF_F_SG ;
469
578
u8 * qc , tid , txf ;
470
579
471
580
snap_ip_tcp = 8 + skb_transport_header (skb ) - skb_network_header (skb ) +
@@ -484,6 +593,19 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
484
593
goto segment ;
485
594
}
486
595
596
+ /*
597
+ * Do not build AMSDU for IPv6 with extension headers.
598
+ * ask stack to segment and checkum the generated MPDUs for us.
599
+ */
600
+ if (skb -> protocol == htons (ETH_P_IPV6 ) &&
601
+ ((struct ipv6hdr * )skb_network_header (skb ))-> nexthdr !=
602
+ IPPROTO_TCP ) {
603
+ num_subframes = 1 ;
604
+ pad = 0 ;
605
+ netdev_features &= ~NETIF_F_CSUM_MASK ;
606
+ goto segment ;
607
+ }
608
+
487
609
/*
488
610
* No need to lock amsdu_in_ampdu_allowed since it can't be modified
489
611
* during an BA session.
@@ -577,7 +699,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
577
699
skb_shinfo (skb )-> gso_size = num_subframes * mss ;
578
700
memcpy (cb , skb -> cb , sizeof (cb ));
579
701
580
- next = skb_gso_segment (skb , NETIF_F_CSUM_MASK | NETIF_F_SG );
702
+ next = skb_gso_segment (skb , netdev_features );
581
703
skb_shinfo (skb )-> gso_size = mss ;
582
704
if (WARN_ON_ONCE (IS_ERR (next )))
583
705
return - EINVAL ;
0 commit comments