ravb: Add Tx checksum offload support for GbEth
authorBiju Das <biju.das.jz@bp.renesas.com>
Wed, 7 Feb 2024 09:28:38 +0000 (09:28 +0000)
committerJakub Kicinski <kuba@kernel.org>
Fri, 9 Feb 2024 03:06:37 +0000 (19:06 -0800)
TOE has hardware support for calculating IP header and TCP/UDP/ICMP
checksum for both IPv4 and IPv6.

Add Tx checksum offload supported by TOE for IPv4 and TCP/UDP.

For Tx, the result of checksum calculation is set to the checksum field of
each IPv4 Header/TCP/UDP/ICMP of ethernet frames. For the unsupported
frames, those fields are not changed. If a transmission frame is an UDPv4
frame and its checksum value in the UDP header field is 0x0000, TOE does
not calculate checksum for UDP part of this frame as it is optional
function as per standards.

We can test this functionality by the below commands

ethtool -K eth0 tx on --> to turn on Tx checksum offload
ethtool -K eth0 tx off --> to turn off Tx checksum offload

Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
Reviewed-by: Sergey Shtylyov <s.shtylyov@omp.ru>
Link: https://lore.kernel.org/r/20240207092838.160627-3-biju.das.jz@bp.renesas.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/renesas/ravb.h
drivers/net/ethernet/renesas/ravb_main.c

index b98677c7c8e136a1cf0337ab10bebef11c0b0003..35e642fc4b2a52a815ef38663eabd50373c46784 100644 (file)
@@ -208,6 +208,7 @@ enum ravb_reg {
 
        /* TOE registers (RZ/G2L only) */
        CSR0    = 0x0800,
+       CSR1    = 0x0804,
        CSR2    = 0x0808,
 };
 
@@ -981,6 +982,20 @@ enum CSR0_BIT {
        CSR0_RPE        = 0x00000020,
 };
 
+enum CSR1_BIT {
+       CSR1_TIP4       = 0x00000001,
+       CSR1_TTCP4      = 0x00000010,
+       CSR1_TUDP4      = 0x00000020,
+       CSR1_TICMP4     = 0x00000040,
+       CSR1_TTCP6      = 0x00100000,
+       CSR1_TUDP6      = 0x00200000,
+       CSR1_TICMP6     = 0x00400000,
+       CSR1_THOP       = 0x01000000,
+       CSR1_TROUT      = 0x02000000,
+       CSR1_TAHD       = 0x04000000,
+       CSR1_TDHD       = 0x08000000,
+};
+
 enum CSR2_BIT {
        CSR2_RIP4       = 0x00000001,
        CSR2_RTCP4      = 0x00000010,
index 3e0c7977f2f808ae780b3dd2c4052329b23dbfc7..f9a1e9038dbf16717b8ac3b813bee7d35f48b79a 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/spinlock.h>
 #include <linux/reset.h>
 #include <linux/math64.h>
+#include <net/ip.h>
 
 #include "ravb.h"
 
@@ -514,16 +515,28 @@ error:
 
 static void ravb_csum_init_gbeth(struct net_device *ndev)
 {
-       if (!(ndev->features & NETIF_F_RXCSUM))
+       bool tx_enable = ndev->features & NETIF_F_HW_CSUM;
+       bool rx_enable = ndev->features & NETIF_F_RXCSUM;
+
+       if (!(tx_enable || rx_enable))
                goto done;
 
        ravb_write(ndev, 0, CSR0);
-       if (ravb_wait(ndev, CSR0, CSR0_RPE, 0)) {
+       if (ravb_wait(ndev, CSR0, CSR0_TPE | CSR0_RPE, 0)) {
                netdev_err(ndev, "Timeout enabling hardware checksum\n");
-               ndev->features &= ~NETIF_F_RXCSUM;
+
+               if (tx_enable)
+                       ndev->features &= ~NETIF_F_HW_CSUM;
+
+               if (rx_enable)
+                       ndev->features &= ~NETIF_F_RXCSUM;
        } else {
-               ravb_write(ndev, CSR2_RIP4 | CSR2_RTCP4 | CSR2_RUDP4 | CSR2_RICMP4,
-                          CSR2);
+               if (tx_enable)
+                       ravb_write(ndev, CSR1_TIP4 | CSR1_TTCP4 | CSR1_TUDP4, CSR1);
+
+               if (rx_enable)
+                       ravb_write(ndev, CSR2_RIP4 | CSR2_RTCP4 | CSR2_RUDP4 | CSR2_RICMP4,
+                                  CSR2);
        }
 
 done:
@@ -2053,6 +2066,36 @@ out_unlock:
        rtnl_unlock();
 }
 
+static bool ravb_can_tx_csum_gbeth(struct sk_buff *skb)
+{
+       struct iphdr *ip = ip_hdr(skb);
+
+       /* TODO: Need to add support for VLAN tag 802.1Q */
+       if (skb_vlan_tag_present(skb))
+               return false;
+
+       /* TODO: Need to add hardware checksum for IPv6 */
+       if (skb->protocol != htons(ETH_P_IP))
+               return false;
+
+       switch (ip->protocol) {
+       case IPPROTO_TCP:
+               break;
+       case IPPROTO_UDP:
+               /* If the checksum value in the UDP header field is 0, TOE does
+                * not calculate checksum for UDP part of this frame as it is
+                * optional function as per standards.
+                */
+               if (udp_hdr(skb)->check == 0)
+                       return false;
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
 /* Packet transmit function for Ethernet AVB */
 static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
@@ -2068,6 +2111,9 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        u32 entry;
        u32 len;
 
+       if (skb->ip_summed == CHECKSUM_PARTIAL && !ravb_can_tx_csum_gbeth(skb))
+               skb_checksum_help(skb);
+
        spin_lock_irqsave(&priv->lock, flags);
        if (priv->cur_tx[q] - priv->dirty_tx[q] > (priv->num_tx_ring[q] - 1) *
            num_tx_desc) {
@@ -2473,6 +2519,17 @@ static int ravb_set_features_gbeth(struct net_device *ndev,
                        goto done;
        }
 
+       if (changed & NETIF_F_HW_CSUM) {
+               if (features & NETIF_F_HW_CSUM)
+                       val = CSR1_TIP4 | CSR1_TTCP4 | CSR1_TUDP4;
+               else
+                       val = 0;
+
+               ret = ravb_endisable_csum_gbeth(ndev, CSR1, val, CSR0_TPE);
+               if (ret)
+                       goto done;
+       }
+
        ndev->features = features;
 done:
        spin_unlock_irqrestore(&priv->lock, flags);
@@ -2657,8 +2714,8 @@ static const struct ravb_hw_info gbeth_hw_info = {
        .emac_init = ravb_emac_init_gbeth,
        .gstrings_stats = ravb_gstrings_stats_gbeth,
        .gstrings_size = sizeof(ravb_gstrings_stats_gbeth),
-       .net_hw_features = NETIF_F_RXCSUM,
-       .net_features = NETIF_F_RXCSUM,
+       .net_hw_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM,
+       .net_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM,
        .stats_len = ARRAY_SIZE(ravb_gstrings_stats_gbeth),
        .max_rx_len = ALIGN(GBETH_RX_BUFF_MAX, RAVB_ALIGN),
        .tccr_mask = TCCR_TSRQ0,