Commit ab78df75 authored by Santiago Leon's avatar Santiago Leon Committed by David S. Miller

ibmveth: Enable IPv6 checksum offload

This patch enables TCP checksum offload support for IPv6 on ibmveth.
This completely eliminates the generation and checking of the checksum
for IPv6 packets that are completely virtual and never touch a physical
network.  A basic TCPIPV6_STREAM netperf run showed a ~30% throughput
improvement when an MTU of 64000 was used.

This featured is enabled by default, as is the case for IPv4 checksum
offload.  When checksum offload is enabled the driver will negotiate
IPv4 and IPv6 offload with the firmware separately and enable what
is available.  As long as either IPv4 or IPv6 offload is supported
and enabled the device will report that checksum offload is enabled.
The device stats, available through ethtool, will display which
checksum offload features are supported/enabled by firmware.

Performance testing against a stock kernel shows no regression for IPv4
or IPv6 in terms of throughput or processor utilization with checksum
disabled or enabled.
Signed-off-by: default avatarRobert Jennings <rcj@linux.vnet.ibm.com>
Signed-off-by: default avatarSantiago Leon <santil@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bc4c6f54
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <asm/hvcall.h> #include <asm/hvcall.h>
...@@ -148,6 +149,8 @@ struct ibmveth_stat ibmveth_stats[] = { ...@@ -148,6 +149,8 @@ struct ibmveth_stat ibmveth_stats[] = {
{ "rx_no_buffer", IBMVETH_STAT_OFF(rx_no_buffer) }, { "rx_no_buffer", IBMVETH_STAT_OFF(rx_no_buffer) },
{ "tx_map_failed", IBMVETH_STAT_OFF(tx_map_failed) }, { "tx_map_failed", IBMVETH_STAT_OFF(tx_map_failed) },
{ "tx_send_failed", IBMVETH_STAT_OFF(tx_send_failed) }, { "tx_send_failed", IBMVETH_STAT_OFF(tx_send_failed) },
{ "fw_enabled_ipv4_csum", IBMVETH_STAT_OFF(fw_ipv4_csum_support) },
{ "fw_enabled_ipv6_csum", IBMVETH_STAT_OFF(fw_ipv6_csum_support) },
}; };
/* simple methods of getting data from the current rxq entry */ /* simple methods of getting data from the current rxq entry */
...@@ -773,6 +776,7 @@ static void ibmveth_set_rx_csum_flags(struct net_device *dev, u32 data) ...@@ -773,6 +776,7 @@ static void ibmveth_set_rx_csum_flags(struct net_device *dev, u32 data)
*/ */
adapter->rx_csum = 0; adapter->rx_csum = 0;
dev->features &= ~NETIF_F_IP_CSUM; dev->features &= ~NETIF_F_IP_CSUM;
dev->features &= ~NETIF_F_IPV6_CSUM;
} }
} }
...@@ -781,10 +785,15 @@ static void ibmveth_set_tx_csum_flags(struct net_device *dev, u32 data) ...@@ -781,10 +785,15 @@ static void ibmveth_set_tx_csum_flags(struct net_device *dev, u32 data)
struct ibmveth_adapter *adapter = netdev_priv(dev); struct ibmveth_adapter *adapter = netdev_priv(dev);
if (data) { if (data) {
if (adapter->fw_ipv4_csum_support)
dev->features |= NETIF_F_IP_CSUM; dev->features |= NETIF_F_IP_CSUM;
if (adapter->fw_ipv6_csum_support)
dev->features |= NETIF_F_IPV6_CSUM;
adapter->rx_csum = 1; adapter->rx_csum = 1;
} else } else {
dev->features &= ~NETIF_F_IP_CSUM; dev->features &= ~NETIF_F_IP_CSUM;
dev->features &= ~NETIF_F_IPV6_CSUM;
}
} }
static int ibmveth_set_csum_offload(struct net_device *dev, u32 data, static int ibmveth_set_csum_offload(struct net_device *dev, u32 data,
...@@ -792,7 +801,8 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data, ...@@ -792,7 +801,8 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data,
{ {
struct ibmveth_adapter *adapter = netdev_priv(dev); struct ibmveth_adapter *adapter = netdev_priv(dev);
unsigned long set_attr, clr_attr, ret_attr; unsigned long set_attr, clr_attr, ret_attr;
long ret; unsigned long set_attr6, clr_attr6;
long ret, ret6;
int rc1 = 0, rc2 = 0; int rc1 = 0, rc2 = 0;
int restart = 0; int restart = 0;
...@@ -806,10 +816,13 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data, ...@@ -806,10 +816,13 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data,
set_attr = 0; set_attr = 0;
clr_attr = 0; clr_attr = 0;
if (data) if (data) {
set_attr = IBMVETH_ILLAN_IPV4_TCP_CSUM; set_attr = IBMVETH_ILLAN_IPV4_TCP_CSUM;
else set_attr6 = IBMVETH_ILLAN_IPV6_TCP_CSUM;
} else {
clr_attr = IBMVETH_ILLAN_IPV4_TCP_CSUM; clr_attr = IBMVETH_ILLAN_IPV4_TCP_CSUM;
clr_attr6 = IBMVETH_ILLAN_IPV6_TCP_CSUM;
}
ret = h_illan_attributes(adapter->vdev->unit_address, 0, 0, &ret_attr); ret = h_illan_attributes(adapter->vdev->unit_address, 0, 0, &ret_attr);
...@@ -820,14 +833,33 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data, ...@@ -820,14 +833,33 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data,
set_attr, &ret_attr); set_attr, &ret_attr);
if (ret != H_SUCCESS) { if (ret != H_SUCCESS) {
rc1 = -EIO; ibmveth_error_printk("unable to change IPv4 checksum "
ibmveth_error_printk("unable to change checksum offload settings." "offload settings. %d rc=%ld\n",
" %d rc=%ld\n", data, ret); data, ret);
ret = h_illan_attributes(adapter->vdev->unit_address, ret = h_illan_attributes(adapter->vdev->unit_address,
set_attr, clr_attr, &ret_attr); set_attr, clr_attr, &ret_attr);
} else } else
adapter->fw_ipv4_csum_support = data;
ret6 = h_illan_attributes(adapter->vdev->unit_address,
clr_attr6, set_attr6, &ret_attr);
if (ret6 != H_SUCCESS) {
ibmveth_error_printk("unable to change IPv6 checksum "
"offload settings. %d rc=%ld\n",
data, ret);
ret = h_illan_attributes(adapter->vdev->unit_address,
set_attr6, clr_attr6,
&ret_attr);
} else
adapter->fw_ipv6_csum_support = data;
if (ret == H_SUCCESS || ret6 == H_SUCCESS)
done(dev, data); done(dev, data);
else
rc1 = -EIO;
} else { } else {
rc1 = -EIO; rc1 = -EIO;
ibmveth_error_printk("unable to change checksum offload settings." ibmveth_error_printk("unable to change checksum offload settings."
...@@ -855,9 +887,9 @@ static int ibmveth_set_tx_csum(struct net_device *dev, u32 data) ...@@ -855,9 +887,9 @@ static int ibmveth_set_tx_csum(struct net_device *dev, u32 data)
struct ibmveth_adapter *adapter = netdev_priv(dev); struct ibmveth_adapter *adapter = netdev_priv(dev);
int rc = 0; int rc = 0;
if (data && (dev->features & NETIF_F_IP_CSUM)) if (data && (dev->features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)))
return 0; return 0;
if (!data && !(dev->features & NETIF_F_IP_CSUM)) if (!data && !(dev->features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)))
return 0; return 0;
if (data && !adapter->rx_csum) if (data && !adapter->rx_csum)
...@@ -975,7 +1007,12 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb, ...@@ -975,7 +1007,12 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
/* veth can't checksum offload UDP */ /* veth can't checksum offload UDP */
if (skb->ip_summed == CHECKSUM_PARTIAL && if (skb->ip_summed == CHECKSUM_PARTIAL &&
ip_hdr(skb)->protocol != IPPROTO_TCP && skb_checksum_help(skb)) { ((skb->protocol == htons(ETH_P_IP) &&
ip_hdr(skb)->protocol != IPPROTO_TCP) ||
(skb->protocol == htons(ETH_P_IPV6) &&
ipv6_hdr(skb)->nexthdr != IPPROTO_TCP)) &&
skb_checksum_help(skb)) {
ibmveth_error_printk("tx: failed to checksum packet\n"); ibmveth_error_printk("tx: failed to checksum packet\n");
netdev->stats.tx_dropped++; netdev->stats.tx_dropped++;
goto out; goto out;
......
...@@ -149,6 +149,8 @@ struct ibmveth_adapter { ...@@ -149,6 +149,8 @@ struct ibmveth_adapter {
void *bounce_buffer; void *bounce_buffer;
dma_addr_t bounce_buffer_dma; dma_addr_t bounce_buffer_dma;
u64 fw_ipv6_csum_support;
u64 fw_ipv4_csum_support;
/* adapter specific stats */ /* adapter specific stats */
u64 replenish_task_cycles; u64 replenish_task_cycles;
u64 replenish_no_mem; u64 replenish_no_mem;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment