aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShani Michaeli <shanim@mellanox.com>2014-11-09 06:51:53 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-11 13:20:02 -0500
commitf8c6455bb04b944edb69e9b074e28efee2c56bdd (patch)
tree9f4f031bb3a33ea164aa2014294c027c478e1d7e
parentdd65beac48a5259945846956d4b27344dfb73bd9 (diff)
net/mlx4_en: Extend checksum offloading by CHECKSUM COMPLETE
When processing received traffic, pass CHECKSUM_COMPLETE status to the stack, with calculated checksum for non TCP/UDP packets (such as GRE or ICMP). Although the stack expects checksum which doesn't include the pseudo header, the HW adds it. To address that, we are subtracting the pseudo header checksum from the checksum value provided by the HW. In the IPv6 case, we also compute/add the IP header checksum which is not added by the HW for such packets. Cc: Jerry Chu <hkchu@google.com> Signed-off-by: Shani Michaeli <shanim@mellanox.com> Signed-off-by: Matan Barak <matanb@mellanox.com> Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_port.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c126
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h5
-rw-r--r--include/linux/mlx4/device.h1
7 files changed, 142 insertions, 8 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index 8ea4d5be7376..6c643230a5ed 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -115,7 +115,7 @@ static const char main_strings[][ETH_GSTRING_LEN] = {
115 "tso_packets", 115 "tso_packets",
116 "xmit_more", 116 "xmit_more",
117 "queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_failed", 117 "queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_failed",
118 "rx_csum_good", "rx_csum_none", "tx_chksum_offload", 118 "rx_csum_good", "rx_csum_none", "rx_csum_complete", "tx_chksum_offload",
119 119
120 /* packet statistics */ 120 /* packet statistics */
121 "broadcast", "rx_prio_0", "rx_prio_1", "rx_prio_2", "rx_prio_3", 121 "broadcast", "rx_prio_0", "rx_prio_1", "rx_prio_2", "rx_prio_3",
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 0efbae90f1ba..d1eb25dbff56 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -1893,6 +1893,7 @@ static void mlx4_en_clear_stats(struct net_device *dev)
1893 priv->rx_ring[i]->packets = 0; 1893 priv->rx_ring[i]->packets = 0;
1894 priv->rx_ring[i]->csum_ok = 0; 1894 priv->rx_ring[i]->csum_ok = 0;
1895 priv->rx_ring[i]->csum_none = 0; 1895 priv->rx_ring[i]->csum_none = 0;
1896 priv->rx_ring[i]->csum_complete = 0;
1896 } 1897 }
1897} 1898}
1898 1899
@@ -2503,6 +2504,10 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
2503 /* Query for default mac and max mtu */ 2504 /* Query for default mac and max mtu */
2504 priv->max_mtu = mdev->dev->caps.eth_mtu_cap[priv->port]; 2505 priv->max_mtu = mdev->dev->caps.eth_mtu_cap[priv->port];
2505 2506
2507 if (mdev->dev->caps.rx_checksum_flags_port[priv->port] &
2508 MLX4_RX_CSUM_MODE_VAL_NON_TCP_UDP)
2509 priv->flags |= MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP;
2510
2506 /* Set default MAC */ 2511 /* Set default MAC */
2507 dev->addr_len = ETH_ALEN; 2512 dev->addr_len = ETH_ALEN;
2508 mlx4_en_u64_to_mac(dev->dev_addr, mdev->dev->caps.def_mac[priv->port]); 2513 mlx4_en_u64_to_mac(dev->dev_addr, mdev->dev->caps.def_mac[priv->port]);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c
index 134b12e17da5..6cb80072af6c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c
@@ -155,11 +155,13 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset)
155 stats->rx_bytes = 0; 155 stats->rx_bytes = 0;
156 priv->port_stats.rx_chksum_good = 0; 156 priv->port_stats.rx_chksum_good = 0;
157 priv->port_stats.rx_chksum_none = 0; 157 priv->port_stats.rx_chksum_none = 0;
158 priv->port_stats.rx_chksum_complete = 0;
158 for (i = 0; i < priv->rx_ring_num; i++) { 159 for (i = 0; i < priv->rx_ring_num; i++) {
159 stats->rx_packets += priv->rx_ring[i]->packets; 160 stats->rx_packets += priv->rx_ring[i]->packets;
160 stats->rx_bytes += priv->rx_ring[i]->bytes; 161 stats->rx_bytes += priv->rx_ring[i]->bytes;
161 priv->port_stats.rx_chksum_good += priv->rx_ring[i]->csum_ok; 162 priv->port_stats.rx_chksum_good += priv->rx_ring[i]->csum_ok;
162 priv->port_stats.rx_chksum_none += priv->rx_ring[i]->csum_none; 163 priv->port_stats.rx_chksum_none += priv->rx_ring[i]->csum_none;
164 priv->port_stats.rx_chksum_complete += priv->rx_ring[i]->csum_complete;
163 } 165 }
164 stats->tx_packets = 0; 166 stats->tx_packets = 0;
165 stats->tx_bytes = 0; 167 stats->tx_bytes = 0;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index df6d352c4e54..ccd95177ea7c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -42,6 +42,10 @@
42#include <linux/vmalloc.h> 42#include <linux/vmalloc.h>
43#include <linux/irq.h> 43#include <linux/irq.h>
44 44
45#if IS_ENABLED(CONFIG_IPV6)
46#include <net/ip6_checksum.h>
47#endif
48
45#include "mlx4_en.h" 49#include "mlx4_en.h"
46 50
47static int mlx4_alloc_pages(struct mlx4_en_priv *priv, 51static int mlx4_alloc_pages(struct mlx4_en_priv *priv,
@@ -643,6 +647,86 @@ static void mlx4_en_refill_rx_buffers(struct mlx4_en_priv *priv,
643 } 647 }
644} 648}
645 649
650/* When hardware doesn't strip the vlan, we need to calculate the checksum
651 * over it and add it to the hardware's checksum calculation
652 */
653static inline __wsum get_fixed_vlan_csum(__wsum hw_checksum,
654 struct vlan_hdr *vlanh)
655{
656 return csum_add(hw_checksum, *(__wsum *)vlanh);
657}
658
659/* Although the stack expects checksum which doesn't include the pseudo
660 * header, the HW adds it. To address that, we are subtracting the pseudo
661 * header checksum from the checksum value provided by the HW.
662 */
663static void get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb,
664 struct iphdr *iph)
665{
666 __u16 length_for_csum = 0;
667 __wsum csum_pseudo_header = 0;
668
669 length_for_csum = (be16_to_cpu(iph->tot_len) - (iph->ihl << 2));
670 csum_pseudo_header = csum_tcpudp_nofold(iph->saddr, iph->daddr,
671 length_for_csum, iph->protocol, 0);
672 skb->csum = csum_sub(hw_checksum, csum_pseudo_header);
673}
674
675#if IS_ENABLED(CONFIG_IPV6)
676/* In IPv6 packets, besides subtracting the pseudo header checksum,
677 * we also compute/add the IP header checksum which
678 * is not added by the HW.
679 */
680static int get_fixed_ipv6_csum(__wsum hw_checksum, struct sk_buff *skb,
681 struct ipv6hdr *ipv6h)
682{
683 __wsum csum_pseudo_hdr = 0;
684
685 if (ipv6h->nexthdr == IPPROTO_FRAGMENT || ipv6h->nexthdr == IPPROTO_HOPOPTS)
686 return -1;
687 hw_checksum = csum_add(hw_checksum, (__force __wsum)(ipv6h->nexthdr << 8));
688
689 csum_pseudo_hdr = csum_partial(&ipv6h->saddr,
690 sizeof(ipv6h->saddr) + sizeof(ipv6h->daddr), 0);
691 csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ipv6h->payload_len);
692 csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ntohs(ipv6h->nexthdr));
693
694 skb->csum = csum_sub(hw_checksum, csum_pseudo_hdr);
695 skb->csum = csum_add(skb->csum, csum_partial(ipv6h, sizeof(struct ipv6hdr), 0));
696 return 0;
697}
698#endif
699static int check_csum(struct mlx4_cqe *cqe, struct sk_buff *skb, void *va,
700 int hwtstamp_rx_filter)
701{
702 __wsum hw_checksum = 0;
703
704 void *hdr = (u8 *)va + sizeof(struct ethhdr);
705
706 hw_checksum = csum_unfold((__force __sum16)cqe->checksum);
707
708 if (((struct ethhdr *)va)->h_proto == htons(ETH_P_8021Q) &&
709 hwtstamp_rx_filter != HWTSTAMP_FILTER_NONE) {
710 /* next protocol non IPv4 or IPv6 */
711 if (((struct vlan_hdr *)hdr)->h_vlan_encapsulated_proto
712 != htons(ETH_P_IP) &&
713 ((struct vlan_hdr *)hdr)->h_vlan_encapsulated_proto
714 != htons(ETH_P_IPV6))
715 return -1;
716 hw_checksum = get_fixed_vlan_csum(hw_checksum, hdr);
717 hdr += sizeof(struct vlan_hdr);
718 }
719
720 if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV4))
721 get_fixed_ipv4_csum(hw_checksum, skb, hdr);
722#if IS_ENABLED(CONFIG_IPV6)
723 else if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV6))
724 if (get_fixed_ipv6_csum(hw_checksum, skb, hdr))
725 return -1;
726#endif
727 return 0;
728}
729
646int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) 730int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget)
647{ 731{
648 struct mlx4_en_priv *priv = netdev_priv(dev); 732 struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -744,13 +828,26 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
744 (cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_L2_TUNNEL)); 828 (cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_L2_TUNNEL));
745 829
746 if (likely(dev->features & NETIF_F_RXCSUM)) { 830 if (likely(dev->features & NETIF_F_RXCSUM)) {
747 if ((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) && 831 if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_TCP |
748 (cqe->checksum == cpu_to_be16(0xffff))) { 832 MLX4_CQE_STATUS_UDP)) {
749 ring->csum_ok++; 833 if ((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) &&
750 ip_summed = CHECKSUM_UNNECESSARY; 834 cqe->checksum == cpu_to_be16(0xffff)) {
835 ip_summed = CHECKSUM_UNNECESSARY;
836 ring->csum_ok++;
837 } else {
838 ip_summed = CHECKSUM_NONE;
839 ring->csum_none++;
840 }
751 } else { 841 } else {
752 ip_summed = CHECKSUM_NONE; 842 if (priv->flags & MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP &&
753 ring->csum_none++; 843 (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV4 |
844 MLX4_CQE_STATUS_IPV6))) {
845 ip_summed = CHECKSUM_COMPLETE;
846 ring->csum_complete++;
847 } else {
848 ip_summed = CHECKSUM_NONE;
849 ring->csum_none++;
850 }
754 } 851 }
755 } else { 852 } else {
756 ip_summed = CHECKSUM_NONE; 853 ip_summed = CHECKSUM_NONE;
@@ -776,6 +873,15 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
776 if (!nr) 873 if (!nr)
777 goto next; 874 goto next;
778 875
876 if (ip_summed == CHECKSUM_COMPLETE) {
877 void *va = skb_frag_address(skb_shinfo(gro_skb)->frags);
878 if (check_csum(cqe, gro_skb, va, ring->hwtstamp_rx_filter)) {
879 ip_summed = CHECKSUM_NONE;
880 ring->csum_none++;
881 ring->csum_complete--;
882 }
883 }
884
779 skb_shinfo(gro_skb)->nr_frags = nr; 885 skb_shinfo(gro_skb)->nr_frags = nr;
780 gro_skb->len = length; 886 gro_skb->len = length;
781 gro_skb->data_len = length; 887 gro_skb->data_len = length;
@@ -822,6 +928,14 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
822 goto next; 928 goto next;
823 } 929 }
824 930
931 if (ip_summed == CHECKSUM_COMPLETE) {
932 if (check_csum(cqe, skb, skb->data, ring->hwtstamp_rx_filter)) {
933 ip_summed = CHECKSUM_NONE;
934 ring->csum_complete--;
935 ring->csum_none++;
936 }
937 }
938
825 skb->ip_summed = ip_summed; 939 skb->ip_summed = ip_summed;
826 skb->protocol = eth_type_trans(skb, dev); 940 skb->protocol = eth_type_trans(skb, dev);
827 skb_record_rx_queue(skb, cq->ring); 941 skb_record_rx_queue(skb, cq->ring);
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 9f821964a1b9..2f6ba420ac03 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -1629,6 +1629,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
1629 struct mlx4_init_hca_param init_hca; 1629 struct mlx4_init_hca_param init_hca;
1630 u64 icm_size; 1630 u64 icm_size;
1631 int err; 1631 int err;
1632 struct mlx4_config_dev_params params;
1632 1633
1633 if (!mlx4_is_slave(dev)) { 1634 if (!mlx4_is_slave(dev)) {
1634 err = mlx4_QUERY_FW(dev); 1635 err = mlx4_QUERY_FW(dev);
@@ -1762,6 +1763,14 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
1762 goto unmap_bf; 1763 goto unmap_bf;
1763 } 1764 }
1764 1765
1766 /* Query CONFIG_DEV parameters */
1767 err = mlx4_config_dev_retrieval(dev, &params);
1768 if (err && err != -ENOTSUPP) {
1769 mlx4_err(dev, "Failed to query CONFIG_DEV parameters\n");
1770 } else if (!err) {
1771 dev->caps.rx_checksum_flags_port[1] = params.rx_csum_flags_port_1;
1772 dev->caps.rx_checksum_flags_port[2] = params.rx_csum_flags_port_2;
1773 }
1765 priv->eq_table.inta_pin = adapter.inta_pin; 1774 priv->eq_table.inta_pin = adapter.inta_pin;
1766 memcpy(dev->board_id, adapter.board_id, sizeof dev->board_id); 1775 memcpy(dev->board_id, adapter.board_id, sizeof dev->board_id);
1767 1776
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index ef83d127f406..de456749ffae 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -326,6 +326,7 @@ struct mlx4_en_rx_ring {
326#endif 326#endif
327 unsigned long csum_ok; 327 unsigned long csum_ok;
328 unsigned long csum_none; 328 unsigned long csum_none;
329 unsigned long csum_complete;
329 int hwtstamp_rx_filter; 330 int hwtstamp_rx_filter;
330 cpumask_var_t affinity_mask; 331 cpumask_var_t affinity_mask;
331}; 332};
@@ -449,6 +450,7 @@ struct mlx4_en_port_stats {
449 unsigned long rx_alloc_failed; 450 unsigned long rx_alloc_failed;
450 unsigned long rx_chksum_good; 451 unsigned long rx_chksum_good;
451 unsigned long rx_chksum_none; 452 unsigned long rx_chksum_none;
453 unsigned long rx_chksum_complete;
452 unsigned long tx_chksum_offload; 454 unsigned long tx_chksum_offload;
453#define NUM_PORT_STATS 9 455#define NUM_PORT_STATS 9
454}; 456};
@@ -507,7 +509,8 @@ enum {
507 MLX4_EN_FLAG_ENABLE_HW_LOOPBACK = (1 << 2), 509 MLX4_EN_FLAG_ENABLE_HW_LOOPBACK = (1 << 2),
508 /* whether we need to drop packets that hardware loopback-ed */ 510 /* whether we need to drop packets that hardware loopback-ed */
509 MLX4_EN_FLAG_RX_FILTER_NEEDED = (1 << 3), 511 MLX4_EN_FLAG_RX_FILTER_NEEDED = (1 << 3),
510 MLX4_EN_FLAG_FORCE_PROMISC = (1 << 4) 512 MLX4_EN_FLAG_FORCE_PROMISC = (1 << 4),
513 MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP = (1 << 5),
511}; 514};
512 515
513#define MLX4_EN_MAC_HASH_SIZE (1 << BITS_PER_BYTE) 516#define MLX4_EN_MAC_HASH_SIZE (1 << BITS_PER_BYTE)
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index 5cc5eac47d1b..3d9bff00f24a 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -497,6 +497,7 @@ struct mlx4_caps {
497 u16 hca_core_clock; 497 u16 hca_core_clock;
498 u64 phys_port_id[MLX4_MAX_PORTS + 1]; 498 u64 phys_port_id[MLX4_MAX_PORTS + 1];
499 int tunnel_offload_mode; 499 int tunnel_offload_mode;
500 u8 rx_checksum_flags_port[MLX4_MAX_PORTS + 1];
500}; 501};
501 502
502struct mlx4_buf_list { 503struct mlx4_buf_list {