Skip to content

Commit 5e6a98d

Browse files
sara-segrumbach
authored andcommitted
iwlwifi: mvm: enable TCP/UDP checksum support for 9000 family
Declare and enable support of RX and TX checksum for 9000 family. Configure offload_assist in the TX cmd accordingly to support TX csum. Signed-off-by: Sara Sharon <[email protected]> Signed-off-by: Emmanuel Grumbach <[email protected]>
1 parent 728e825 commit 5e6a98d

File tree

4 files changed

+133
-7
lines changed

4 files changed

+133
-7
lines changed

drivers/net/wireless/intel/iwlwifi/iwl-9000.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ static const struct iwl_tt_params iwl9000_tt_params = {
137137
.dccm2_len = IWL9000_DCCM2_LEN, \
138138
.smem_offset = IWL9000_SMEM_OFFSET, \
139139
.smem_len = IWL9000_SMEM_LEN, \
140+
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
140141
.thermal_params = &iwl9000_tt_params, \
141142
.apmg_not_supported = true, \
142143
.mq_rx_supported = true, \

drivers/net/wireless/intel/iwlwifi/iwl-config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ enum iwl_led_mode {
131131
#define IWL_MAX_WD_TIMEOUT 120000
132132

133133
#define IWL_DEFAULT_MAX_TX_POWER 22
134+
#define IWL_TX_CSUM_NETIF_FLAGS (NETIF_F_IPV6_CSUM | NETIF_F_IP_CSUM |\
135+
NETIF_F_TSO | NETIF_F_TSO6)
134136

135137
/* Antenna presence definitions */
136138
#define ANT_NONE 0x0

drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -665,12 +665,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
665665
}
666666

667667
hw->netdev_features |= mvm->cfg->features;
668-
if (!iwl_mvm_is_csum_supported(mvm))
669-
hw->netdev_features &= ~NETIF_F_RXCSUM;
670-
671-
if (IWL_MVM_SW_TX_CSUM_OFFLOAD)
672-
hw->netdev_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
673-
NETIF_F_TSO | NETIF_F_TSO6;
668+
if (!iwl_mvm_is_csum_supported(mvm)) {
669+
hw->netdev_features &= ~(IWL_TX_CSUM_NETIF_FLAGS |
670+
NETIF_F_RXCSUM);
671+
/* We may support SW TX CSUM */
672+
if (IWL_MVM_SW_TX_CSUM_OFFLOAD)
673+
hw->netdev_features |= IWL_TX_CSUM_NETIF_FLAGS;
674+
}
674675

675676
ret = ieee80211_register_hw(mvm->hw);
676677
if (ret)

drivers/net/wireless/intel/iwlwifi/mvm/tx.c

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#include <linux/etherdevice.h>
6868
#include <linux/tcp.h>
6969
#include <net/ip.h>
70+
#include <net/ipv6.h>
7071

7172
#include "iwl-trans.h"
7273
#include "iwl-eeprom-parse.h"
@@ -98,6 +99,111 @@ iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
9899
addr, tid, ssn);
99100
}
100101

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+
101207
/*
102208
* Sets most of the Tx cmd's fields
103209
*/
@@ -196,6 +302,8 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
196302
if (ieee80211_hdrlen(fc) % 4 &&
197303
!(tx_cmd->offload_assist & cpu_to_le16(BIT(TX_CMD_OFFLD_AMSDU))))
198304
tx_cmd->offload_assist |= cpu_to_le16(BIT(TX_CMD_OFFLD_PAD));
305+
306+
iwl_mvm_tx_csum(mvm, skb, hdr, info, tx_cmd);
199307
}
200308

201309
/*
@@ -466,6 +574,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
466574
u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
467575
u16 amsdu_add, snap_ip_tcp, pad, i = 0;
468576
unsigned int dbg_max_amsdu_len;
577+
netdev_features_t netdev_features = NETIF_F_CSUM_MASK | NETIF_F_SG;
469578
u8 *qc, tid, txf;
470579

471580
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,
484593
goto segment;
485594
}
486595

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+
487609
/*
488610
* No need to lock amsdu_in_ampdu_allowed since it can't be modified
489611
* during an BA session.
@@ -577,7 +699,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
577699
skb_shinfo(skb)->gso_size = num_subframes * mss;
578700
memcpy(cb, skb->cb, sizeof(cb));
579701

580-
next = skb_gso_segment(skb, NETIF_F_CSUM_MASK | NETIF_F_SG);
702+
next = skb_gso_segment(skb, netdev_features);
581703
skb_shinfo(skb)->gso_size = mss;
582704
if (WARN_ON_ONCE(IS_ERR(next)))
583705
return -EINVAL;

0 commit comments

Comments
 (0)