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@gmx.de> Link: https://github.com/openwrt/openwrt/pull/21778 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
parent
1b2f8975f7
commit
41300fd88a
@ -114,7 +114,7 @@ struct notify_b {
|
|||||||
u32 reserved2[8];
|
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 */
|
/* cpu_tag[0] is reserved on the RTL83XX SoCs */
|
||||||
h->cpu_tag[1] = 0x0400; /* BIT 10: RTL8380_CPU_TAG */
|
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;
|
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 */
|
/* cpu_tag[0] is reserved on the RTL83XX SoCs */
|
||||||
h->cpu_tag[1] = 0x0100; /* RTL8390_CPU_TAG marker */
|
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;
|
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 */
|
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;
|
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 */
|
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++)
|
for (int i = 0; i < ctrl->rxrings; i++)
|
||||||
sw_w32(KSEG1ADDR(&ring->rx_r[i]), ctrl->r->dma_rx_base + i * 4);
|
sw_w32(KSEG1ADDR(&ring->rx_r[i]), ctrl->r->dma_rx_base + i * 4);
|
||||||
|
|
||||||
for (int i = 0; i < TXRINGS; i++)
|
for (int r = 0; r < RTETH_TX_RINGS; r++)
|
||||||
sw_w32(KSEG1ADDR(&ring->tx_r[i]), ctrl->r->dma_tx_base + i * 4);
|
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)
|
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);
|
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(netdev);
|
||||||
struct rteth_ctrl *ctrl = netdev_priv(dev);
|
int slot, len = skb->len, dest_port = -1;
|
||||||
struct ring_b *ring = ctrl->membase;
|
int ring = skb_get_queue_mapping(skb);
|
||||||
int ret;
|
struct device *dev = &ctrl->pdev->dev;
|
||||||
unsigned long flags;
|
struct rteth_packet *packet;
|
||||||
struct p_hdr *h;
|
dma_addr_t packet_dma;
|
||||||
int dest_port = -1;
|
|
||||||
int q = skb_get_queue_mapping(skb) % TXRINGS;
|
|
||||||
|
|
||||||
if (q) /* Check for high prio queue */
|
if (netdev_uses_dsa(netdev) &&
|
||||||
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) &&
|
|
||||||
skb->data[len - 4] == 0x80 &&
|
skb->data[len - 4] == 0x80 &&
|
||||||
skb->data[len - 3] < ctrl->r->cpu_port &&
|
skb->data[len - 3] < ctrl->r->cpu_port &&
|
||||||
skb->data[len - 2] == 0x10 &&
|
skb->data[len - 2] == 0x10 &&
|
||||||
skb->data[len - 1] == 0x00) {
|
skb->data[len - 1] == 0x00) {
|
||||||
/* Reuse tag space for CRC if possible */
|
/* DSA tag, reuse space for CRC */
|
||||||
dest_port = skb->data[len - 3];
|
dest_port = skb->data[len - 3];
|
||||||
skb->data[len - 4] = skb->data[len - 3] = skb->data[len - 2] = skb->data[len - 1] = 0x00;
|
skb->data[len - 4] = skb->data[len - 3] = 0;
|
||||||
len -= 4;
|
skb->data[len - 2] = skb->data[len - 1] = 0;
|
||||||
}
|
|
||||||
|
|
||||||
len += 4; /* Add space for CRC */
|
|
||||||
|
|
||||||
if (skb_padto(skb, len)) {
|
|
||||||
ret = NETDEV_TX_OK;
|
|
||||||
goto txdone;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dest_port >= 0)
|
|
||||||
ctrl->r->create_tx_header(h, dest_port, skb->priority >> 1);
|
|
||||||
|
|
||||||
/* Copy packet data to tx buffer */
|
|
||||||
memcpy((void *)KSEG1ADDR(h->buf), skb->data, len);
|
|
||||||
/* Make sure packet data is visible to ASIC */
|
|
||||||
wmb();
|
|
||||||
|
|
||||||
/* Hand over to switch */
|
|
||||||
ring->tx_r[q][ring->c_tx[q]] |= 1;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
if ((val & 0xc) == 0xc)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
dev_warn(&ctrl->pdev->dev, "Data is owned by switch\n");
|
/* No DSA tag, add space for CRC */
|
||||||
ret = NETDEV_TX_BUSY;
|
len += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
txdone:
|
if (unlikely(skb_padto(skb, len))) {
|
||||||
spin_unlock_irqrestore(&ctrl->lock, flags);
|
netdev->stats.tx_errors++;
|
||||||
|
dev_warn(dev, "skb pad failed\n");
|
||||||
|
|
||||||
return ret;
|
return NETDEV_TX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot = ctrl->tx_data[ring].slot;
|
||||||
|
packet = &ctrl->tx_data[ring].packet[slot];
|
||||||
|
packet_dma = ctrl->tx_data[ring].ring[slot];
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return NETDEV_TX_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
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++;
|
||||||
|
|
||||||
|
return NETDEV_TX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 NETDEV_TX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
|
static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
|
||||||
|
|||||||
@ -435,7 +435,7 @@ struct rteth_config {
|
|||||||
u32 (*get_mac_tx_pause_sts)(int port);
|
u32 (*get_mac_tx_pause_sts)(int port);
|
||||||
int mac;
|
int mac;
|
||||||
int l2_tbl_flush_ctrl;
|
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);
|
bool (*decode_tag)(struct p_hdr *h, struct dsa_tag *tag);
|
||||||
void (*hw_reset)(struct rteth_ctrl *ctrl);
|
void (*hw_reset)(struct rteth_ctrl *ctrl);
|
||||||
int (*init_mac)(struct rteth_ctrl *ctrl);
|
int (*init_mac)(struct rteth_ctrl *ctrl);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user