diff --git a/target/linux/gemini/patches-6.6/0003-net-ethernet-cortina-Locking-fixes.patch b/target/linux/gemini/patches-6.6/0003-net-ethernet-cortina-Locking-fixes.patch new file mode 100644 index 00000000000..661e9287c09 --- /dev/null +++ b/target/linux/gemini/patches-6.6/0003-net-ethernet-cortina-Locking-fixes.patch @@ -0,0 +1,73 @@ +From 81889eb2b37bc21df4ff259441e8fc12d4f27cd9 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Thu, 9 May 2024 08:48:31 +0200 +Subject: [PATCH] net: ethernet: cortina: Locking fixes + +This fixes a probably long standing problem in the Cortina +Gemini ethernet driver: there are some paths in the code +where the IRQ registers are written without taking the proper +locks. + +Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") +Signed-off-by: Linus Walleij +--- + drivers/net/ethernet/cortina/gemini.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1107,10 +1107,13 @@ static void gmac_tx_irq_enable(struct ne + { + struct gemini_ethernet_port *port = netdev_priv(netdev); + struct gemini_ethernet *geth = port->geth; ++ unsigned long flags; + u32 val, mask; + + netdev_dbg(netdev, "%s device %d\n", __func__, netdev->dev_id); + ++ spin_lock_irqsave(&geth->irq_lock, flags); ++ + mask = GMAC0_IRQ0_TXQ0_INTS << (6 * netdev->dev_id + txq); + + if (en) +@@ -1119,6 +1122,8 @@ static void gmac_tx_irq_enable(struct ne + val = readl(geth->base + GLOBAL_INTERRUPT_ENABLE_0_REG); + val = en ? val | mask : val & ~mask; + writel(val, geth->base + GLOBAL_INTERRUPT_ENABLE_0_REG); ++ ++ spin_unlock_irqrestore(&geth->irq_lock, flags); + } + + static void gmac_tx_irq(struct net_device *netdev, unsigned int txq_num) +@@ -1415,15 +1420,19 @@ static unsigned int gmac_rx(struct net_d + union gmac_rxdesc_3 word3; + struct page *page = NULL; + unsigned int page_offs; ++ unsigned long flags; + unsigned short r, w; + union dma_rwptr rw; + dma_addr_t mapping; + int frag_nr = 0; + ++ spin_lock_irqsave(&geth->irq_lock, flags); + rw.bits32 = readl(ptr_reg); + /* Reset interrupt as all packages until here are taken into account */ + writel(DEFAULT_Q0_INT_BIT << netdev->dev_id, + geth->base + GLOBAL_INTERRUPT_STATUS_1_REG); ++ spin_unlock_irqrestore(&geth->irq_lock, flags); ++ + r = rw.bits.rptr; + w = rw.bits.wptr; + +@@ -1726,10 +1735,9 @@ static irqreturn_t gmac_irq(int irq, voi + gmac_update_hw_stats(netdev); + + if (val & (GMAC0_RX_OVERRUN_INT_BIT << (netdev->dev_id * 8))) { ++ spin_lock(&geth->irq_lock); + writel(GMAC0_RXDERR_INT_BIT << (netdev->dev_id * 8), + geth->base + GLOBAL_INTERRUPT_STATUS_4_REG); +- +- spin_lock(&geth->irq_lock); + u64_stats_update_begin(&port->ir_stats_syncp); + ++port->stats.rx_fifo_errors; + u64_stats_update_end(&port->ir_stats_syncp); diff --git a/target/linux/gemini/patches-6.6/0004-net-ethernet-cortina-Restore-TSO-support.patch b/target/linux/gemini/patches-6.6/0004-net-ethernet-cortina-Restore-TSO-support.patch new file mode 100644 index 00000000000..809941a95d4 --- /dev/null +++ b/target/linux/gemini/patches-6.6/0004-net-ethernet-cortina-Restore-TSO-support.patch @@ -0,0 +1,124 @@ +From 30fcba19ed88997a2909e4a68b4d39ff371357c3 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 1 May 2024 21:46:31 +0200 +Subject: [PATCH 1/5] net: ethernet: cortina: Restore TSO support + +An earlier commit deleted the TSO support in the Cortina Gemini +driver because the driver was confusing gso_size and MTU, +probably because what the Linux kernel calls "gso_size" was +called "MTU" in the datasheet. + +Restore the functionality properly reading the gso_size from +the skbuff. + +Tested with iperf3, running a server on a different machine +and client on the device with the cortina gemini ethernet: + +Connecting to host 192.168.1.2, port 5201 +60008000.ethernet-port eth0: segment offloading mss = 05ea len=1c8a +60008000.ethernet-port eth0: segment offloading mss = 05ea len=1c8a +60008000.ethernet-port eth0: segment offloading mss = 05ea len=27da +60008000.ethernet-port eth0: segment offloading mss = 05ea len=0b92 +60008000.ethernet-port eth0: segment offloading mss = 05ea len=2bda +(...) + +(The hardware MSS 0x05ea here includes the ethernet headers.) + +If I disable all segment offloading on the receiving host and +dump packets using tcpdump -xx like this: + +ethtool -K enp2s0 gro off gso off tso off +tcpdump -xx -i enp2s0 host 192.168.1.136 + +I get segmented packages such as this when running iperf3: + +23:16:54.024139 IP OpenWrt.lan.59168 > Fecusia.targus-getdata1: +Flags [.], seq 1486:2934, ack 1, win 4198, +options [nop,nop,TS val 3886192908 ecr 3601341877], length 1448 +0x0000: fc34 9701 a0c6 14d6 4da8 3c4f 0800 4500 +0x0010: 05dc 16a0 4000 4006 9aa1 c0a8 0188 c0a8 +0x0020: 0102 e720 1451 ff25 9822 4c52 29cf 8010 +0x0030: 1066 ac8c 0000 0101 080a e7a2 990c d6a8 +(...) +0x05c0: 5e49 e109 fe8c 4617 5e18 7a82 7eae d647 +0x05d0: e8ee ae64 dc88 c897 3f8a 07a4 3a33 6b1b +0x05e0: 3501 a30f 2758 cc44 4b4a + +Several such packets often follow after each other verifying +the segmentation into 0x05a8 (1448) byte packages also on the +reveiving end. As can be seen, the ethernet frames are +0x05ea (1514) in size. + +Performance with iperf3 before this patch: ~15.5 Mbit/s +Performance with iperf3 after this patch: ~175 Mbit/s + +This was running a 60 second test (twice) the best measurement +was 179 Mbit/s. + +For comparison if I run iperf3 with UDP I get around 1.05 Mbit/s +both before and after this patch. + +While this is a gigabit ethernet interface, the CPU is a cheap +D-Link DIR-685 router (based on the ARMv5 Faraday FA526 at +~50 MHz), and the software is not supposed to drive traffic, +as the device has a DSA chip, so this kind of numbers can be +expected. + +Fixes: ac631873c9e7 ("net: ethernet: cortina: Drop TSO support") +Reviewed-by: Eric Dumazet +Signed-off-by: Linus Walleij +--- + drivers/net/ethernet/cortina/gemini.c | 23 +++++++++++++++++++---- + 1 file changed, 19 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -79,7 +79,8 @@ MODULE_PARM_DESC(debug, "Debug level (0= + #define GMAC0_IRQ4_8 (GMAC0_MIB_INT_BIT | GMAC0_RX_OVERRUN_INT_BIT) + + #define GMAC_OFFLOAD_FEATURES (NETIF_F_SG | NETIF_F_IP_CSUM | \ +- NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM) ++ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | \ ++ NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6) + + /** + * struct gmac_queue_page - page buffer per-page info +@@ -1148,13 +1149,25 @@ static int gmac_map_tx_bufs(struct net_d + skb_frag_t *skb_frag; + dma_addr_t mapping; + void *buffer; ++ u16 mss; + int ret; + +- /* TODO: implement proper TSO using MTU in word3 */ + word1 = skb->len; + word3 = SOF_BIT; + +- if (skb->len >= ETH_FRAME_LEN) { ++ mss = skb_shinfo(skb)->gso_size; ++ if (mss) { ++ /* This means we are dealing with TCP and skb->len is the ++ * sum total of all the segments. The TSO will deal with ++ * chopping this up for us. ++ */ ++ /* The accelerator needs the full frame size here */ ++ mss += skb_tcp_all_headers(skb); ++ netdev_dbg(netdev, "segment offloading mss = %04x len=%04x\n", ++ mss, skb->len); ++ word1 |= TSS_MTU_ENABLE_BIT; ++ word3 |= mss; ++ } else 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 +@@ -1169,7 +1182,9 @@ static int gmac_map_tx_bufs(struct net_d + return ret; + } + word1 |= TSS_BYPASS_BIT; +- } else if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ } ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) { + int tcp = 0; + + /* We do not switch off the checksumming on non TCP/UDP diff --git a/target/linux/gemini/patches-6.6/0005-net-ethernet-cortina-Use-TSO-also-on-common-TCP.patch b/target/linux/gemini/patches-6.6/0005-net-ethernet-cortina-Use-TSO-also-on-common-TCP.patch new file mode 100644 index 00000000000..c690b8fddb3 --- /dev/null +++ b/target/linux/gemini/patches-6.6/0005-net-ethernet-cortina-Use-TSO-also-on-common-TCP.patch @@ -0,0 +1,95 @@ +From 91fb8a7328dda827bc6c0da240a1eb17028416cd Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Thu, 9 May 2024 23:59:28 +0200 +Subject: [PATCH 2/5] net: ethernet: cortina: Use TSO also on common TCP + +It is possible to push the segment offloader to also +process non-segmented frames: just pass the skb->len +or desired MSS to the offloader and it will handle them. + +This is especially good if the user sets up the MTU +and the frames get big, because the checksumming engine +cannot handle any frames bigger than 1518 bytes, so +segmenting them all to be at max that will be helpful +for the hardware, which only need to quirk odd frames +such as big UDP ping packets. + +The vendor driver always uses the TSO like this, and +the driver seems more stable after this, so apparently +the hardware may have been engineered to always use +the TSO on anything it can handle. + +Signed-off-by: Linus Walleij +--- + drivers/net/ethernet/cortina/gemini.c | 31 +++++++++++++++++++++------ + 1 file changed, 24 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -1148,6 +1148,7 @@ static int gmac_map_tx_bufs(struct net_d + struct gmac_txdesc *txd; + skb_frag_t *skb_frag; + dma_addr_t mapping; ++ bool tcp = false; + void *buffer; + u16 mss; + int ret; +@@ -1155,6 +1156,13 @@ static int gmac_map_tx_bufs(struct net_d + word1 = skb->len; + word3 = SOF_BIT; + ++ /* Determine if we are doing TCP */ ++ if (skb->protocol == htons(ETH_P_IP)) ++ tcp = (ip_hdr(skb)->protocol == IPPROTO_TCP); ++ else ++ /* IPv6 */ ++ tcp = (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP); ++ + mss = skb_shinfo(skb)->gso_size; + if (mss) { + /* This means we are dealing with TCP and skb->len is the +@@ -1167,6 +1175,20 @@ static int gmac_map_tx_bufs(struct net_d + mss, skb->len); + word1 |= TSS_MTU_ENABLE_BIT; + word3 |= mss; ++ } else if (tcp) { ++ /* Even if we are not using TSO, use the segment offloader ++ * for transferring the TCP frame: the TSO engine will deal ++ * with chopping up frames that exceed ETH_DATA_LEN which ++ * the checksumming engine cannot handle (see below) into ++ * manageable chunks. It flawlessly deals with quite big ++ * frames and frames containing custom DSA EtherTypes. ++ */ ++ mss = netdev->mtu + skb_tcp_all_headers(skb); ++ mss = min(mss, skb->len); ++ netdev_dbg(netdev, "botched TSO len %04x mtu %04x mss %04x\n", ++ skb->len, netdev->mtu, mss); ++ word1 |= TSS_MTU_ENABLE_BIT; ++ word3 |= mss; + } else 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 +@@ -1185,21 +1207,16 @@ static int gmac_map_tx_bufs(struct net_d + } + + 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)) { ++ if (skb->protocol == htons(ETH_P_IP)) + word1 |= TSS_IP_CHKSUM_BIT; +- tcp = ip_hdr(skb)->protocol == IPPROTO_TCP; +- } else { /* IPv6 */ ++ else + word1 |= TSS_IPV6_ENABLE_BIT; +- tcp = ipv6_hdr(skb)->nexthdr == IPPROTO_TCP; +- } + + word1 |= tcp ? TSS_TCP_CHKSUM_BIT : TSS_UDP_CHKSUM_BIT; + } diff --git a/target/linux/gemini/patches-6.6/0006-net-ethernet-cortina-Rename-adjust-link-callback.patch b/target/linux/gemini/patches-6.6/0006-net-ethernet-cortina-Rename-adjust-link-callback.patch new file mode 100644 index 00000000000..bbdef8fefc2 --- /dev/null +++ b/target/linux/gemini/patches-6.6/0006-net-ethernet-cortina-Rename-adjust-link-callback.patch @@ -0,0 +1,36 @@ +From fa01c904b844e6033445f75b0b4d46a8e83b6086 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Fri, 10 May 2024 19:48:27 +0200 +Subject: [PATCH 3/5] net: ethernet: cortina: Rename adjust link callback + +The callback passed to of_phy_get_and_connect() in the +Cortina Gemini driver is called "gmac_speed_set" which is +archaic, rename it to "gmac_adjust_link" following the +pattern of most other drivers. + +Reviewed-by: Andrew Lunn +Signed-off-by: Linus Walleij +--- + drivers/net/ethernet/cortina/gemini.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -288,7 +288,7 @@ static void gmac_set_flow_control(struct + spin_unlock_irqrestore(&port->config_lock, flags); + } + +-static void gmac_speed_set(struct net_device *netdev) ++static void gmac_adjust_link(struct net_device *netdev) + { + struct gemini_ethernet_port *port = netdev_priv(netdev); + struct phy_device *phydev = netdev->phydev; +@@ -367,7 +367,7 @@ static int gmac_setup_phy(struct net_dev + + phy = of_phy_get_and_connect(netdev, + dev->of_node, +- gmac_speed_set); ++ gmac_adjust_link); + if (!phy) + return -ENODEV; + netdev->phydev = phy; diff --git a/target/linux/gemini/patches-6.6/0007-net-ethernet-cortina-Use-negotiated-TX-RX-pause.patch b/target/linux/gemini/patches-6.6/0007-net-ethernet-cortina-Use-negotiated-TX-RX-pause.patch new file mode 100644 index 00000000000..a1b8707f725 --- /dev/null +++ b/target/linux/gemini/patches-6.6/0007-net-ethernet-cortina-Use-negotiated-TX-RX-pause.patch @@ -0,0 +1,46 @@ +From 50ac9765c674bac803719c6b8294670edc6df31d Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Fri, 10 May 2024 19:44:39 +0200 +Subject: [PATCH 4/5] net: ethernet: cortina: Use negotiated TX/RX pause + +Instead of directly poking into registers of the PHY, use +the existing function to query phylib about this directly. + +Suggested-by: Andrew Lunn +Reviewed-by: Andrew Lunn +Signed-off-by: Linus Walleij +--- + drivers/net/ethernet/cortina/gemini.c | 15 +++++---------- + 1 file changed, 5 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -293,8 +293,8 @@ static void gmac_adjust_link(struct net_ + struct gemini_ethernet_port *port = netdev_priv(netdev); + struct phy_device *phydev = netdev->phydev; + union gmac_status status, old_status; +- int pause_tx = 0; +- int pause_rx = 0; ++ bool pause_tx = false; ++ bool pause_rx = false; + + status.bits32 = readl(port->gmac_base + GMAC_STATUS); + old_status.bits32 = status.bits32; +@@ -329,14 +329,9 @@ static void gmac_adjust_link(struct net_ + } + + if (phydev->duplex == DUPLEX_FULL) { +- u16 lcladv = phy_read(phydev, MII_ADVERTISE); +- u16 rmtadv = phy_read(phydev, MII_LPA); +- u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); +- +- if (cap & FLOW_CTRL_RX) +- pause_rx = 1; +- if (cap & FLOW_CTRL_TX) +- pause_tx = 1; ++ phy_get_pause(phydev, &pause_tx, &pause_rx); ++ netdev_dbg(netdev, "set negotiated pause params pause TX = %s, pause RX = %s\n", ++ pause_tx ? "ON" : "OFF", pause_rx ? "ON" : "OFF"); + } + + gmac_set_flow_control(netdev, pause_tx, pause_rx); diff --git a/target/linux/gemini/patches-6.6/0008-net-ethernet-cortina-Implement-.set_pauseparam.patch b/target/linux/gemini/patches-6.6/0008-net-ethernet-cortina-Implement-.set_pauseparam.patch new file mode 100644 index 00000000000..ad7594e855c --- /dev/null +++ b/target/linux/gemini/patches-6.6/0008-net-ethernet-cortina-Implement-.set_pauseparam.patch @@ -0,0 +1,46 @@ +From 4eed4b87f17d10b7586349c13c3a30f9c24c9ba4 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 8 May 2024 23:21:17 +0200 +Subject: [PATCH 5/5] net: ethernet: cortina: Implement .set_pauseparam() + +The Cortina Gemini ethernet can very well set up TX or RX +pausing, so add this functionality to the driver in a +.set_pauseparam() callback. Essentially just call down to +phylib and let phylib deal with this, .adjust_link() +will respect the setting from phylib. + +Signed-off-by: Linus Walleij +--- + drivers/net/ethernet/cortina/gemini.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/net/ethernet/cortina/gemini.c ++++ b/drivers/net/ethernet/cortina/gemini.c +@@ -2143,6 +2143,19 @@ static void gmac_get_pauseparam(struct n + pparam->autoneg = true; + } + ++static int gmac_set_pauseparam(struct net_device *netdev, ++ struct ethtool_pauseparam *pparam) ++{ ++ struct phy_device *phydev = netdev->phydev; ++ ++ if (!pparam->autoneg) ++ return -EOPNOTSUPP; ++ ++ phy_set_asym_pause(phydev, pparam->rx_pause, pparam->tx_pause); ++ ++ return 0; ++} ++ + static void gmac_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *rp, + struct kernel_ethtool_ringparam *kernel_rp, +@@ -2263,6 +2276,7 @@ static const struct ethtool_ops gmac_351 + .set_link_ksettings = gmac_set_ksettings, + .nway_reset = gmac_nway_reset, + .get_pauseparam = gmac_get_pauseparam, ++ .set_pauseparam = gmac_set_pauseparam, + .get_ringparam = gmac_get_ringparam, + .set_ringparam = gmac_set_ringparam, + .get_coalesce = gmac_get_coalesce,