Merge branch 'add-hw-checksum-offload-support-for-rz-g2l-gbethernet-ip'

Biju Das says:

====================
Add HW checksum offload support for RZ/G2L GbEthernet IP

This patch series aims to add HW checksum offload supported by TOE module
found on the RZ/G2L Gb ethernet IP.

TOE has hardware support for calculating IP header and TCP/UDP/ICMP
checksum for both IPv4 and IPv6.

For Rx, the 4-byte result of checksum calculation is attached to the
Ethernet frames.First 2-bytes is result of IPv4 header checksum and next
2-bytes is TCP/UDP/ICMP checksum.

If a frame does not have checksum error, 0x0000 is attached as checksum
calculation result. For unsupported frames 0xFFFF is attached as checksum
calculation result. In case of an IPv6 packet, IPv4 checksum is always set
to 0xFFFF.

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.

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

Results of iperf3 in Mbps

RZ/V2L:
TCP(Tx/Rx) results with checksum offload Enabled:	{921,932}
TCP(Tx/Rx) results with checksum offload Disabled:	{867,612}

UDP(Tx/Rx) results with checksum offload Enabled:	{950,946}
UDP(Tx/Rx) results with checksum offload Disabled:	{952,920}

RZ/G2L:
TCP(Tx/Rx) results with checksum offload Enabled:	{920,936}
TCP(Tx/Rx) results with checksum offload Disabled:	{871,626}

UDP(Tx/Rx) results with checksum offload Enabled:	{953,950}
UDP(Tx/Rx) results with checksum offload Disabled:	{954,920}

RZ/G2LC:
TCP(Tx/Rx) results with checksum offload Enabled:	{927,936}
TCP(Tx/Rx) results with checksum offload Disabled:	{889,626}

UDP(Tx/Rx) results with checksum offload Enabled:	{950,946}
UDP(Tx/Rx) results with checksum offload Disabled:	{949,944}
====================

Link: https://lore.kernel.org/r/20240207092838.160627-1-biju.das.jz@bp.renesas.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2024-02-08 19:06:39 -08:00
commit 458aabfd3b
2 changed files with 179 additions and 4 deletions

View file

@ -205,7 +205,11 @@ enum ravb_reg {
TLFRCR = 0x0758,
RFCR = 0x0760,
MAFCR = 0x0778,
CSR0 = 0x0800, /* RZ/G2L only */
/* TOE registers (RZ/G2L only) */
CSR0 = 0x0800,
CSR1 = 0x0804,
CSR2 = 0x0808,
};
@ -978,6 +982,34 @@ 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,
CSR2_RUDP4 = 0x00000020,
CSR2_RICMP4 = 0x00000040,
CSR2_RTCP6 = 0x00100000,
CSR2_RUDP6 = 0x00200000,
CSR2_RICMP6 = 0x00400000,
CSR2_RHOP = 0x01000000,
CSR2_RROUT = 0x02000000,
CSR2_RAHD = 0x04000000,
CSR2_RDHD = 0x08000000,
};
#define DBAT_ENTRY_NUM 22
#define RX_QUEUE_OFFSET 4
#define NUM_RX_QUEUE 2

View file

@ -29,6 +29,7 @@
#include <linux/spinlock.h>
#include <linux/reset.h>
#include <linux/math64.h>
#include <net/ip.h>
#include "ravb.h"
@ -512,6 +513,36 @@ static int ravb_ring_init(struct net_device *ndev, int q)
return -ENOMEM;
}
static void ravb_csum_init_gbeth(struct net_device *ndev)
{
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_TPE | CSR0_RPE, 0)) {
netdev_err(ndev, "Timeout enabling hardware checksum\n");
if (tx_enable)
ndev->features &= ~NETIF_F_HW_CSUM;
if (rx_enable)
ndev->features &= ~NETIF_F_RXCSUM;
} else {
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:
ravb_write(ndev, CSR0_TPE | CSR0_RPE, CSR0);
}
static void ravb_emac_init_gbeth(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
@ -543,7 +574,8 @@ static void ravb_emac_init_gbeth(struct net_device *ndev)
/* E-MAC status register clear */
ravb_write(ndev, ECSR_ICD | ECSR_LCHNG | ECSR_PFRI, ECSR);
ravb_write(ndev, CSR0_TPE | CSR0_RPE, CSR0);
ravb_csum_init_gbeth(ndev);
/* E-MAC interrupt enable register */
ravb_write(ndev, ECSIPR_ICDIP, ECSIPR);
@ -724,6 +756,30 @@ static void ravb_get_tx_tstamp(struct net_device *ndev)
}
}
static void ravb_rx_csum_gbeth(struct sk_buff *skb)
{
__wsum csum_ip_hdr, csum_proto;
u8 *hw_csum;
/* The hardware checksum status is contained in sizeof(__sum16) * 2 = 4
* bytes appended to packet data. First 2 bytes is ip header checksum
* and last 2 bytes is protocol checksum.
*/
if (unlikely(skb->len < sizeof(__sum16) * 2))
return;
hw_csum = skb_tail_pointer(skb) - sizeof(__sum16);
csum_proto = csum_unfold((__force __sum16)get_unaligned_le16(hw_csum));
hw_csum -= sizeof(__sum16);
csum_ip_hdr = csum_unfold((__force __sum16)get_unaligned_le16(hw_csum));
skb_trim(skb, skb->len - 2 * sizeof(__sum16));
/* TODO: IPV6 Rx checksum */
if (skb->protocol == htons(ETH_P_IP) && !csum_ip_hdr && !csum_proto)
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
static void ravb_rx_csum(struct sk_buff *skb)
{
u8 *hw_csum;
@ -809,6 +865,8 @@ static bool ravb_rx_gbeth(struct net_device *ndev, int *quota, int q)
skb = ravb_get_skb_gbeth(ndev, entry, desc);
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, ndev);
if (ndev->features & NETIF_F_RXCSUM)
ravb_rx_csum_gbeth(skb);
napi_gro_receive(&priv->napi[q], skb);
stats->rx_packets++;
stats->rx_bytes += pkt_len;
@ -836,6 +894,8 @@ static bool ravb_rx_gbeth(struct net_device *ndev, int *quota, int q)
dev_kfree_skb(skb);
priv->rx_1st_skb->protocol =
eth_type_trans(priv->rx_1st_skb, ndev);
if (ndev->features & NETIF_F_RXCSUM)
ravb_rx_csum_gbeth(skb);
napi_gro_receive(&priv->napi[q],
priv->rx_1st_skb);
stats->rx_packets++;
@ -2006,6 +2066,36 @@ static void ravb_tx_timeout_work(struct work_struct *work)
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)
{
@ -2021,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) {
@ -2389,11 +2482,59 @@ static void ravb_set_rx_csum(struct net_device *ndev, bool enable)
spin_unlock_irqrestore(&priv->lock, flags);
}
static int ravb_endisable_csum_gbeth(struct net_device *ndev, enum ravb_reg reg,
u32 val, u32 mask)
{
u32 csr0 = CSR0_TPE | CSR0_RPE;
int ret;
ravb_write(ndev, csr0 & ~mask, CSR0);
ret = ravb_wait(ndev, CSR0, mask, 0);
if (!ret)
ravb_write(ndev, val, reg);
ravb_write(ndev, csr0, CSR0);
return ret;
}
static int ravb_set_features_gbeth(struct net_device *ndev,
netdev_features_t features)
{
/* Place holder */
return 0;
netdev_features_t changed = ndev->features ^ features;
struct ravb_private *priv = netdev_priv(ndev);
unsigned long flags;
int ret = 0;
u32 val;
spin_lock_irqsave(&priv->lock, flags);
if (changed & NETIF_F_RXCSUM) {
if (features & NETIF_F_RXCSUM)
val = CSR2_RIP4 | CSR2_RTCP4 | CSR2_RUDP4 | CSR2_RICMP4;
else
val = 0;
ret = ravb_endisable_csum_gbeth(ndev, CSR2, val, CSR0_RPE);
if (ret)
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);
return ret;
}
static int ravb_set_features_rcar(struct net_device *ndev,
@ -2573,6 +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 | 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,