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 <sara.sharon@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
This commit is contained in:
parent
728e825f81
commit
5e6a98dc48
@ -137,6 +137,7 @@ static const struct iwl_tt_params iwl9000_tt_params = {
|
|||||||
.dccm2_len = IWL9000_DCCM2_LEN, \
|
.dccm2_len = IWL9000_DCCM2_LEN, \
|
||||||
.smem_offset = IWL9000_SMEM_OFFSET, \
|
.smem_offset = IWL9000_SMEM_OFFSET, \
|
||||||
.smem_len = IWL9000_SMEM_LEN, \
|
.smem_len = IWL9000_SMEM_LEN, \
|
||||||
|
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \
|
||||||
.thermal_params = &iwl9000_tt_params, \
|
.thermal_params = &iwl9000_tt_params, \
|
||||||
.apmg_not_supported = true, \
|
.apmg_not_supported = true, \
|
||||||
.mq_rx_supported = true, \
|
.mq_rx_supported = true, \
|
||||||
|
@ -131,6 +131,8 @@ enum iwl_led_mode {
|
|||||||
#define IWL_MAX_WD_TIMEOUT 120000
|
#define IWL_MAX_WD_TIMEOUT 120000
|
||||||
|
|
||||||
#define IWL_DEFAULT_MAX_TX_POWER 22
|
#define IWL_DEFAULT_MAX_TX_POWER 22
|
||||||
|
#define IWL_TX_CSUM_NETIF_FLAGS (NETIF_F_IPV6_CSUM | NETIF_F_IP_CSUM |\
|
||||||
|
NETIF_F_TSO | NETIF_F_TSO6)
|
||||||
|
|
||||||
/* Antenna presence definitions */
|
/* Antenna presence definitions */
|
||||||
#define ANT_NONE 0x0
|
#define ANT_NONE 0x0
|
||||||
|
@ -665,12 +665,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
hw->netdev_features |= mvm->cfg->features;
|
hw->netdev_features |= mvm->cfg->features;
|
||||||
if (!iwl_mvm_is_csum_supported(mvm))
|
if (!iwl_mvm_is_csum_supported(mvm)) {
|
||||||
hw->netdev_features &= ~NETIF_F_RXCSUM;
|
hw->netdev_features &= ~(IWL_TX_CSUM_NETIF_FLAGS |
|
||||||
|
NETIF_F_RXCSUM);
|
||||||
if (IWL_MVM_SW_TX_CSUM_OFFLOAD)
|
/* We may support SW TX CSUM */
|
||||||
hw->netdev_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
|
if (IWL_MVM_SW_TX_CSUM_OFFLOAD)
|
||||||
NETIF_F_TSO | NETIF_F_TSO6;
|
hw->netdev_features |= IWL_TX_CSUM_NETIF_FLAGS;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ieee80211_register_hw(mvm->hw);
|
ret = ieee80211_register_hw(mvm->hw);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
#include <net/ip.h>
|
#include <net/ip.h>
|
||||||
|
#include <net/ipv6.h>
|
||||||
|
|
||||||
#include "iwl-trans.h"
|
#include "iwl-trans.h"
|
||||||
#include "iwl-eeprom-parse.h"
|
#include "iwl-eeprom-parse.h"
|
||||||
@ -98,6 +99,111 @@ iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
|
|||||||
addr, tid, ssn);
|
addr, tid, ssn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define OPT_HDR(type, skb, off) \
|
||||||
|
(type *)(skb_network_header(skb) + (off))
|
||||||
|
|
||||||
|
static void iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
|
||||||
|
struct ieee80211_hdr *hdr,
|
||||||
|
struct ieee80211_tx_info *info,
|
||||||
|
struct iwl_tx_cmd *tx_cmd)
|
||||||
|
{
|
||||||
|
#if IS_ENABLED(CONFIG_INET)
|
||||||
|
u16 mh_len = ieee80211_hdrlen(hdr->frame_control);
|
||||||
|
u16 offload_assist = le16_to_cpu(tx_cmd->offload_assist);
|
||||||
|
u8 protocol = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do not compute checksum if already computed or if transport will
|
||||||
|
* compute it
|
||||||
|
*/
|
||||||
|
if (skb->ip_summed != CHECKSUM_PARTIAL || IWL_MVM_SW_TX_CSUM_OFFLOAD)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* We do not expect to be requested to csum stuff we do not support */
|
||||||
|
if (WARN_ONCE(!(mvm->hw->netdev_features & IWL_TX_CSUM_NETIF_FLAGS) ||
|
||||||
|
(skb->protocol != htons(ETH_P_IP) &&
|
||||||
|
skb->protocol != htons(ETH_P_IPV6)),
|
||||||
|
"No support for requested checksum\n")) {
|
||||||
|
skb_checksum_help(skb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb->protocol == htons(ETH_P_IP)) {
|
||||||
|
protocol = ip_hdr(skb)->protocol;
|
||||||
|
} else {
|
||||||
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
struct ipv6hdr *ipv6h =
|
||||||
|
(struct ipv6hdr *)skb_network_header(skb);
|
||||||
|
unsigned int off = sizeof(*ipv6h);
|
||||||
|
|
||||||
|
protocol = ipv6h->nexthdr;
|
||||||
|
while (protocol != NEXTHDR_NONE && ipv6_ext_hdr(protocol)) {
|
||||||
|
/* only supported extension headers */
|
||||||
|
if (protocol != NEXTHDR_ROUTING &&
|
||||||
|
protocol != NEXTHDR_HOP &&
|
||||||
|
protocol != NEXTHDR_DEST &&
|
||||||
|
protocol != NEXTHDR_FRAGMENT) {
|
||||||
|
skb_checksum_help(skb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protocol == NEXTHDR_FRAGMENT) {
|
||||||
|
struct frag_hdr *hp =
|
||||||
|
OPT_HDR(struct frag_hdr, skb, off);
|
||||||
|
|
||||||
|
protocol = hp->nexthdr;
|
||||||
|
off += sizeof(struct frag_hdr);
|
||||||
|
} else {
|
||||||
|
struct ipv6_opt_hdr *hp =
|
||||||
|
OPT_HDR(struct ipv6_opt_hdr, skb, off);
|
||||||
|
|
||||||
|
protocol = hp->nexthdr;
|
||||||
|
off += ipv6_optlen(hp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* if we get here - protocol now should be TCP/UDP */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) {
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
skb_checksum_help(skb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable L4 csum */
|
||||||
|
offload_assist |= BIT(TX_CMD_OFFLD_L4_EN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set offset to IP header (snap).
|
||||||
|
* We don't support tunneling so no need to take care of inner header.
|
||||||
|
* Size is in words.
|
||||||
|
*/
|
||||||
|
offload_assist |= (4 << TX_CMD_OFFLD_IP_HDR);
|
||||||
|
|
||||||
|
/* Do IPv4 csum for AMSDU only (no IP csum for Ipv6) */
|
||||||
|
if (skb->protocol == htons(ETH_P_IP) &&
|
||||||
|
(offload_assist & BIT(TX_CMD_OFFLD_AMSDU))) {
|
||||||
|
ip_hdr(skb)->check = 0;
|
||||||
|
offload_assist |= BIT(TX_CMD_OFFLD_L3_EN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset UDP/TCP header csum */
|
||||||
|
if (protocol == IPPROTO_TCP)
|
||||||
|
tcp_hdr(skb)->check = 0;
|
||||||
|
else
|
||||||
|
udp_hdr(skb)->check = 0;
|
||||||
|
|
||||||
|
/* mac header len should include IV, size is in words */
|
||||||
|
if (info->control.hw_key)
|
||||||
|
mh_len += info->control.hw_key->iv_len;
|
||||||
|
mh_len /= 2;
|
||||||
|
offload_assist |= mh_len << TX_CMD_OFFLD_MH_SIZE;
|
||||||
|
|
||||||
|
tx_cmd->offload_assist = cpu_to_le16(offload_assist);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sets most of the Tx cmd's fields
|
* Sets most of the Tx cmd's fields
|
||||||
*/
|
*/
|
||||||
@ -196,6 +302,8 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
|
|||||||
if (ieee80211_hdrlen(fc) % 4 &&
|
if (ieee80211_hdrlen(fc) % 4 &&
|
||||||
!(tx_cmd->offload_assist & cpu_to_le16(BIT(TX_CMD_OFFLD_AMSDU))))
|
!(tx_cmd->offload_assist & cpu_to_le16(BIT(TX_CMD_OFFLD_AMSDU))))
|
||||||
tx_cmd->offload_assist |= cpu_to_le16(BIT(TX_CMD_OFFLD_PAD));
|
tx_cmd->offload_assist |= cpu_to_le16(BIT(TX_CMD_OFFLD_PAD));
|
||||||
|
|
||||||
|
iwl_mvm_tx_csum(mvm, skb, hdr, info, tx_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -466,6 +574,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
|
|||||||
u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
|
u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
|
||||||
u16 amsdu_add, snap_ip_tcp, pad, i = 0;
|
u16 amsdu_add, snap_ip_tcp, pad, i = 0;
|
||||||
unsigned int dbg_max_amsdu_len;
|
unsigned int dbg_max_amsdu_len;
|
||||||
|
netdev_features_t netdev_features = NETIF_F_CSUM_MASK | NETIF_F_SG;
|
||||||
u8 *qc, tid, txf;
|
u8 *qc, tid, txf;
|
||||||
|
|
||||||
snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) +
|
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,
|
|||||||
goto segment;
|
goto segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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) {
|
||||||
|
num_subframes = 1;
|
||||||
|
pad = 0;
|
||||||
|
netdev_features &= ~NETIF_F_CSUM_MASK;
|
||||||
|
goto segment;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No need to lock amsdu_in_ampdu_allowed since it can't be modified
|
* No need to lock amsdu_in_ampdu_allowed since it can't be modified
|
||||||
* during an BA session.
|
* during an BA session.
|
||||||
@ -577,7 +699,7 @@ segment:
|
|||||||
skb_shinfo(skb)->gso_size = num_subframes * mss;
|
skb_shinfo(skb)->gso_size = num_subframes * mss;
|
||||||
memcpy(cb, skb->cb, sizeof(cb));
|
memcpy(cb, skb->cb, sizeof(cb));
|
||||||
|
|
||||||
next = skb_gso_segment(skb, NETIF_F_CSUM_MASK | NETIF_F_SG);
|
next = skb_gso_segment(skb, netdev_features);
|
||||||
skb_shinfo(skb)->gso_size = mss;
|
skb_shinfo(skb)->gso_size = mss;
|
||||||
if (WARN_ON_ONCE(IS_ERR(next)))
|
if (WARN_ON_ONCE(IS_ERR(next)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
Loading…
Reference in New Issue
Block a user