[openwrt/openwrt] realtek: eth: refactor transmit function

LEDE Commits lede-commits at lists.infradead.org
Sun Feb 1 02:28:05 PST 2026


robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/41300fd88a96037fc1931b424a27618a2a4474ad

commit 41300fd88a96037fc1931b424a27618a2a4474ad
Author: Markus Stockhausen <markus.stockhausen at gmx.de>
AuthorDate: Fri Jan 30 13:58:05 2026 +0100

    realtek: eth: refactor transmit function
    
    Provide a new transmit function. It is cleaner and closer to
    upstream than what we have now. The basic features are:
    
    - Avoid memory moving and keep data in the SKBs
    - Only protect really critcal parts by a lock as transmit
      queues will be only called once by the kernel
    
    Signed-off-by: Markus Stockhausen <markus.stockhausen at gmx.de>
    Link: https://github.com/openwrt/openwrt/pull/21778
    Signed-off-by: Robert Marko <robimarko at gmail.com>
---
 .../files-6.12/drivers/net/ethernet/rtl838x_eth.c  | 162 +++++++++++----------
 .../files-6.12/drivers/net/ethernet/rtl838x_eth.h  |   2 +-
 2 files changed, 84 insertions(+), 80 deletions(-)

diff --git a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c
index f47a8c538a..b4f1b54419 100644
--- a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c
+++ b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c
@@ -114,7 +114,7 @@ struct notify_b {
 	u32			reserved2[8];
 };
 
-static void rteth_838x_create_tx_header(struct p_hdr *h, unsigned int dest_port, int prio)
+static void rteth_838x_create_tx_header(struct rteth_packet *h, unsigned int dest_port, int prio)
 {
 	/* cpu_tag[0] is reserved on the RTL83XX SoCs */
 	h->cpu_tag[1] = 0x0400;  /* BIT 10: RTL8380_CPU_TAG */
@@ -128,7 +128,7 @@ static void rteth_838x_create_tx_header(struct p_hdr *h, unsigned int dest_port,
 		h->cpu_tag[2] |= ((prio & 0x7) | BIT(3)) << 12;
 }
 
-static void rteth_839x_create_tx_header(struct p_hdr *h, unsigned int dest_port, int prio)
+static void rteth_839x_create_tx_header(struct rteth_packet *h, unsigned int dest_port, int prio)
 {
 	/* cpu_tag[0] is reserved on the RTL83XX SoCs */
 	h->cpu_tag[1] = 0x0100; /* RTL8390_CPU_TAG marker */
@@ -149,7 +149,7 @@ static void rteth_839x_create_tx_header(struct p_hdr *h, unsigned int dest_port,
 		h->cpu_tag[2] |= ((prio & 0x7) | BIT(3)) << 8;
 }
 
-static void rteth_930x_create_tx_header(struct p_hdr *h, unsigned int dest_port, int prio)
+static void rteth_930x_create_tx_header(struct rteth_packet *h, unsigned int dest_port, int prio)
 {
 	h->cpu_tag[0] = 0x8000;  /* CPU tag marker */
 
@@ -168,7 +168,7 @@ static void rteth_930x_create_tx_header(struct p_hdr *h, unsigned int dest_port,
 		h->cpu_tag[2] = (BIT(5) | (prio & 0x1f)) << 8;
 }
 
-static void rteth_931x_create_tx_header(struct p_hdr *h, unsigned int dest_port, int prio)
+static void rteth_931x_create_tx_header(struct rteth_packet *h, unsigned int dest_port, int prio)
 {
 	h->cpu_tag[0] = 0x8000;  /* CPU tag marker */
 
@@ -557,8 +557,10 @@ static void rteth_hw_ring_setup(struct rteth_ctrl *ctrl)
 	for (int i = 0; i < ctrl->rxrings; i++)
 		sw_w32(KSEG1ADDR(&ring->rx_r[i]), ctrl->r->dma_rx_base + i * 4);
 
-	for (int i = 0; i < TXRINGS; i++)
-		sw_w32(KSEG1ADDR(&ring->tx_r[i]), ctrl->r->dma_tx_base + i * 4);
+	for (int r = 0; r < RTETH_TX_RINGS; r++)
+		sw_w32(ctrl->tx_dma +
+		       r * sizeof(struct rteth_tx) + offsetof(struct rteth_tx, ring),
+		       ctrl->r->dma_tx_base + r * 4);
 }
 
 static void rtl838x_hw_en_rxtx(struct rteth_ctrl *ctrl)
@@ -967,100 +969,102 @@ static void rteth_tx_timeout(struct net_device *ndev, unsigned int txqueue)
 	spin_unlock_irqrestore(&ctrl->lock, flags);
 }
 
-static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static int rteth_start_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
-	int len;
-	struct rteth_ctrl *ctrl = netdev_priv(dev);
-	struct ring_b *ring = ctrl->membase;
-	int ret;
-	unsigned long flags;
-	struct p_hdr *h;
-	int dest_port = -1;
-	int q = skb_get_queue_mapping(skb) % TXRINGS;
-
-	if (q) /* Check for high prio queue */
-		pr_debug("SKB priority: %d\n", skb->priority);
-
-	spin_lock_irqsave(&ctrl->lock, flags);
-	len = skb->len;
-
-	/* Check for DSA tagging at the end of the buffer */
-	if (netdev_uses_dsa(dev) &&
+	struct rteth_ctrl *ctrl = netdev_priv(netdev);
+	int slot, len = skb->len, dest_port = -1;
+	int ring = skb_get_queue_mapping(skb);
+	struct device *dev = &ctrl->pdev->dev;
+	struct rteth_packet *packet;
+	dma_addr_t packet_dma;
+
+	if (netdev_uses_dsa(netdev) &&
 	    skb->data[len - 4] == 0x80 &&
 	    skb->data[len - 3] < ctrl->r->cpu_port &&
 	    skb->data[len - 2] == 0x10 &&
 	    skb->data[len - 1] == 0x00) {
-		/* Reuse tag space for CRC if possible */
+		/* DSA tag, reuse space for CRC */
 		dest_port = skb->data[len - 3];
-		skb->data[len - 4] = skb->data[len - 3] = skb->data[len - 2] = skb->data[len - 1] = 0x00;
-		len -= 4;
+		skb->data[len - 4] = skb->data[len - 3] = 0;
+		skb->data[len - 2] = skb->data[len - 1] = 0;
+	} else {
+		/* No DSA tag, add space for CRC */
+		len += 4;
 	}
 
-	len += 4; /* Add space for CRC */
+	if (unlikely(skb_padto(skb, len))) {
+		netdev->stats.tx_errors++;
+		dev_warn(dev, "skb pad failed\n");
 
-	if (skb_padto(skb, len)) {
-		ret = NETDEV_TX_OK;
-		goto txdone;
+		return NETDEV_TX_OK;
 	}
 
-	/* We can send this packet if CPU owns the descriptor */
-	if (!(ring->tx_r[q][ring->c_tx[q]] & 0x1)) {
-		/* Set descriptor for tx */
-		h = &ring->tx_header[q][ring->c_tx[q]];
-		h->size = len;
-		h->len = len;
-		/* On RTL8380 SoCs, small packet lengths being sent need adjustments */
-		if (ctrl->r->family_id == RTL8380_FAMILY_ID) {
-			if (len < ETH_ZLEN - 4)
-				h->len -= 4;
-		}
+	slot = ctrl->tx_data[ring].slot;
+	packet = &ctrl->tx_data[ring].packet[slot];
+	packet_dma = ctrl->tx_data[ring].ring[slot];
 
-		if (dest_port >= 0)
-			ctrl->r->create_tx_header(h, dest_port, skb->priority >> 1);
+	if (unlikely(packet_dma & RTETH_OWN_CPU)) {
+		netif_stop_subqueue(netdev, ring);
+		if (net_ratelimit())
+			dev_warn(dev, "tx ring %d busy, waiting for slot %d\n", ring, slot);
 
-		/* Copy packet data to tx buffer */
-		memcpy((void *)KSEG1ADDR(h->buf), skb->data, len);
-		/* Make sure packet data is visible to ASIC */
-		wmb();
+		return NETDEV_TX_BUSY;
+	}
 
-		/* Hand over to switch */
-		ring->tx_r[q][ring->c_tx[q]] |= 1;
+	packet->dma = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
+	if (unlikely(dma_mapping_error(dev, packet->dma))) {
+		dev_kfree_skb_any(skb);
+		netdev->stats.tx_errors++;
 
-		/* Before starting TX, prevent a Lextra bus bug on RTL8380 SoCs */
-		if (ctrl->r->family_id == RTL8380_FAMILY_ID) {
-			for (int i = 0; i < 10; i++) {
-				u32 val = sw_r32(ctrl->r->dma_if_ctrl);
+		return NETDEV_TX_OK;
+	}
 
-				if ((val & 0xc) == 0xc)
-					break;
-			}
-		}
+	if (likely(packet->skb)) {
+		/* cleanup old data of this slot */
+		dma_unmap_single(dev, packet->dma, packet->skb->len, DMA_TO_DEVICE);
+		dev_kfree_skb_any(packet->skb);
+	}
 
-		/* Tell switch to send data */
-		if (ctrl->r->family_id == RTL9310_FAMILY_ID || ctrl->r->family_id == RTL9300_FAMILY_ID) {
-			/* Ring ID q == 0: Low priority, Ring ID = 1: High prio queue */
-			if (!q)
-				sw_w32_mask(0, BIT(2), ctrl->r->dma_if_ctrl);
-			else
-				sw_w32_mask(0, BIT(3), ctrl->r->dma_if_ctrl);
-		} else {
-			sw_w32_mask(0, TX_DO, ctrl->r->dma_if_ctrl);
-		}
+	if (ctrl->r->family_id == RTL8380_FAMILY_ID) {
+		/* On RTL8380 SoCs, small packet lengths being sent need adjustments */
+		if (len < ETH_ZLEN - 4)
+			len -= 4;
+	}
 
-		dev->stats.tx_packets++;
-		dev->stats.tx_bytes += len;
-		dev_kfree_skb(skb);
-		ring->c_tx[q] = (ring->c_tx[q] + 1) % TXRINGLEN;
-		ret = NETDEV_TX_OK;
-	} else {
-		dev_warn(&ctrl->pdev->dev, "Data is owned by switch\n");
-		ret = NETDEV_TX_BUSY;
+	if (dest_port >= 0)
+		ctrl->r->create_tx_header(packet, dest_port, 0); // TODO ok to set prio to 0?
+
+	/* Transfer data and hand packet over to switch */
+	packet->len = len;
+	packet->skb = skb;
+	dma_wmb();
+	ctrl->tx_data[ring].ring[slot] = packet_dma | RTETH_OWN_CPU;
+	ctrl->tx_data[ring].slot = (slot + 1) % RTETH_TX_RING_SIZE;
+	wmb();
+
+	spin_lock(&ctrl->tx_lock);
+
+	/* Before starting TX, prevent a Lexra bus bug on RTL8380 SoCs */
+	if (ctrl->r->family_id == RTL8380_FAMILY_ID) {
+		for (int i = 0; i < 10; i++) {
+			u32 val = sw_r32(ctrl->r->dma_if_ctrl);
+			if ((val & 0xc) == 0xc)
+				break;
+		}
 	}
 
-txdone:
-	spin_unlock_irqrestore(&ctrl->lock, flags);
+	/* Tell switch to send data */
+	if (ctrl->r->family_id == RTL9310_FAMILY_ID || ctrl->r->family_id == RTL9300_FAMILY_ID)
+		sw_w32_mask(0, BIT(2 + ring), ctrl->r->dma_if_ctrl);
+	else
+		sw_w32_mask(0, TX_DO, ctrl->r->dma_if_ctrl);
+
+	netdev->stats.tx_packets++;
+	netdev->stats.tx_bytes += len;
+
+	spin_unlock(&ctrl->tx_lock);
 
-	return ret;
+	return NETDEV_TX_OK;
 }
 
 static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
diff --git a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h
index bb64b88d15..cdd3b61229 100644
--- a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h
+++ b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.h
@@ -435,7 +435,7 @@ struct rteth_config {
 	u32 (*get_mac_tx_pause_sts)(int port);
 	int mac;
 	int l2_tbl_flush_ctrl;
-	void (*create_tx_header)(struct p_hdr *h, unsigned int dest_port, int prio);
+	void (*create_tx_header)(struct rteth_packet *h, unsigned int dest_port, int prio);
 	bool (*decode_tag)(struct p_hdr *h, struct dsa_tag *tag);
 	void (*hw_reset)(struct rteth_ctrl *ctrl);
 	int (*init_mac)(struct rteth_ctrl *ctrl);




More information about the lede-commits mailing list