From: Markus Stockhausen Date: Fri, 30 Jan 2026 12:58:05 +0000 (+0100) Subject: realtek: eth: refactor transmit function X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=41300fd88a96037fc1931b424a27618a2a4474ad;p=thirdparty%2Fopenwrt.git 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 Link: https://github.com/openwrt/openwrt/pull/21778 Signed-off-by: Robert Marko --- 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 f47a8c538ae..b4f1b544198 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 bb64b88d15b..cdd3b612299 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);