Merge branch 'fix-large-frames-in-the-gemini-ethernet-driver'

Linus Walleij says:

====================
Fix large frames in the Gemini ethernet driver

This is the result of a bug hunt for a problem with the
RTL8366RB DSA switch leading me wrong all over the place.

I am indebted to Vladimir Oltean who as usual pointed
out where the real problem was, many thanks!

Tryig to actually use big ("jumbo") frames on this
hardware uncovered the real bugs. Then I tested it on
the DSA switch and it indeed fixes the issue.

To make sure it also works fine with big frames on
non-DSA devices I also copied a large video file over
scp to a device with maximum frame size, the data
was transported in large TCP packets ending up in
0x7ff sized frames using software checksumming at
~2.0 MB/s.

If I set down the MTU to the standard 1500 bytes so
that hardware checksumming is used, the scp transfer
of the same file was slightly lower, ~1.8-1.9 MB/s.

Despite this not being the best test it shows that
we can now stress the hardware with large frames
and that software checksum works fine.

v3: https://lore.kernel.org/r/20231107-gemini-largeframe-fix-v3-0-e3803c080b75@linaro.org
v2: https://lore.kernel.org/r/20231105-gemini-largeframe-fix-v2-0-cd3a5aa6c496@linaro.org
v1: https://lore.kernel.org/r/20231104-gemini-largeframe-fix-v1-0-9c5513f22f33@linaro.org
====================

Link: https://lore.kernel.org/r/20231109-gemini-largeframe-fix-v4-0-6e611528db08@linaro.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2023-11-13 20:58:22 -08:00
commit 334e90b8d7
2 changed files with 31 additions and 18 deletions

View File

@ -432,8 +432,8 @@ static const struct gmac_max_framelen gmac_maxlens[] = {
.val = CONFIG0_MAXLEN_1536,
},
{
.max_l3_len = 1542,
.val = CONFIG0_MAXLEN_1542,
.max_l3_len = 1548,
.val = CONFIG0_MAXLEN_1548,
},
{
.max_l3_len = 9212,
@ -1145,6 +1145,7 @@ static int gmac_map_tx_bufs(struct net_device *netdev, struct sk_buff *skb,
dma_addr_t mapping;
unsigned short mtu;
void *buffer;
int ret;
mtu = ETH_HLEN;
mtu += netdev->mtu;
@ -1159,9 +1160,30 @@ static int gmac_map_tx_bufs(struct net_device *netdev, struct sk_buff *skb,
word3 |= mtu;
}
if (skb->ip_summed != CHECKSUM_NONE) {
if (skb->len >= ETH_FRAME_LEN) {
/* Hardware offloaded checksumming isn't working on frames
* bigger than 1514 bytes. A hypothesis about this is that the
* checksum buffer is only 1518 bytes, so when the frames get
* bigger they get truncated, or the last few bytes get
* overwritten by the FCS.
*
* Just use software checksumming and bypass on bigger frames.
*/
if (skb->ip_summed == CHECKSUM_PARTIAL) {
ret = skb_checksum_help(skb);
if (ret)
return ret;
}
word1 |= TSS_BYPASS_BIT;
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
int tcp = 0;
/* We do not switch off the checksumming on non TCP/UDP
* frames: as is shown from tests, the checksumming engine
* is smart enough to see that a frame is not actually TCP
* or UDP and then just pass it through without any changes
* to the frame.
*/
if (skb->protocol == htons(ETH_P_IP)) {
word1 |= TSS_IP_CHKSUM_BIT;
tcp = ip_hdr(skb)->protocol == IPPROTO_TCP;
@ -1978,15 +2000,6 @@ static int gmac_change_mtu(struct net_device *netdev, int new_mtu)
return 0;
}
static netdev_features_t gmac_fix_features(struct net_device *netdev,
netdev_features_t features)
{
if (netdev->mtu + ETH_HLEN + VLAN_HLEN > MTU_SIZE_BIT_MASK)
features &= ~GMAC_OFFLOAD_FEATURES;
return features;
}
static int gmac_set_features(struct net_device *netdev,
netdev_features_t features)
{
@ -2212,7 +2225,6 @@ static const struct net_device_ops gmac_351x_ops = {
.ndo_set_mac_address = gmac_set_mac_address,
.ndo_get_stats64 = gmac_get_stats64,
.ndo_change_mtu = gmac_change_mtu,
.ndo_fix_features = gmac_fix_features,
.ndo_set_features = gmac_set_features,
};
@ -2464,11 +2476,12 @@ static int gemini_ethernet_port_probe(struct platform_device *pdev)
netdev->hw_features = GMAC_OFFLOAD_FEATURES;
netdev->features |= GMAC_OFFLOAD_FEATURES | NETIF_F_GRO;
/* We can handle jumbo frames up to 10236 bytes so, let's accept
* payloads of 10236 bytes minus VLAN and ethernet header
/* We can receive jumbo frames up to 10236 bytes but only
* transmit 2047 bytes so, let's accept payloads of 2047
* bytes minus VLAN and ethernet header
*/
netdev->min_mtu = ETH_MIN_MTU;
netdev->max_mtu = 10236 - VLAN_ETH_HLEN;
netdev->max_mtu = MTU_SIZE_BIT_MASK - VLAN_ETH_HLEN;
port->freeq_refill = 0;
netif_napi_add(netdev, &port->napi, gmac_napi_poll);

View File

@ -502,7 +502,7 @@ union gmac_txdesc_3 {
#define SOF_BIT 0x80000000
#define EOF_BIT 0x40000000
#define EOFIE_BIT BIT(29)
#define MTU_SIZE_BIT_MASK 0x1fff
#define MTU_SIZE_BIT_MASK 0x7ff /* Max MTU 2047 bytes */
/* GMAC Tx Descriptor */
struct gmac_txdesc {
@ -787,7 +787,7 @@ union gmac_config0 {
#define CONFIG0_MAXLEN_1536 0
#define CONFIG0_MAXLEN_1518 1
#define CONFIG0_MAXLEN_1522 2
#define CONFIG0_MAXLEN_1542 3
#define CONFIG0_MAXLEN_1548 3
#define CONFIG0_MAXLEN_9k 4 /* 9212 */
#define CONFIG0_MAXLEN_10k 5 /* 10236 */
#define CONFIG0_MAXLEN_1518__6 6