Skip to content

Commit 0c316b6

Browse files
nikita-youshgregkh
authored andcommitted
net: renesas: rswitch: fix race window between tx start and complete
[ Upstream commit 0c9547e ] If hardware is already transmitting, it can start handling the descriptor being written to immediately after it observes updated DT field, before the queue is kicked by a write to GWTRC. If the start_xmit() execution is preempted at unfortunate moment, this transmission can complete, and interrupt handled, before gq->cur gets updated. With the current implementation of completion, this will cause the last entry not completed. Fix that by changing completion loop to check DT values directly, instead of depending on gq->cur. Fixes: 3590918 ("net: ethernet: renesas: Add support for "Ethernet Switch"") Signed-off-by: Nikita Yushchenko <[email protected]> Reviewed-by: Yoshihiro Shimoda <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent af327c0 commit 0c316b6

File tree

1 file changed

+8
-8
lines changed

1 file changed

+8
-8
lines changed

drivers/net/ethernet/renesas/rswitch.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -793,13 +793,10 @@ static void rswitch_tx_free(struct net_device *ndev)
793793
struct rswitch_ext_desc *desc;
794794
struct sk_buff *skb;
795795

796-
for (; rswitch_get_num_cur_queues(gq) > 0;
797-
gq->dirty = rswitch_next_queue_index(gq, false, 1)) {
798-
desc = &gq->tx_ring[gq->dirty];
799-
if ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY)
800-
break;
801-
796+
desc = &gq->tx_ring[gq->dirty];
797+
while ((desc->desc.die_dt & DT_MASK) == DT_FEMPTY) {
802798
dma_rmb();
799+
803800
skb = gq->skbs[gq->dirty];
804801
if (skb) {
805802
dma_unmap_single(ndev->dev.parent,
@@ -810,7 +807,10 @@ static void rswitch_tx_free(struct net_device *ndev)
810807
rdev->ndev->stats.tx_packets++;
811808
rdev->ndev->stats.tx_bytes += skb->len;
812809
}
810+
813811
desc->desc.die_dt = DT_EEMPTY;
812+
gq->dirty = rswitch_next_queue_index(gq, false, 1);
813+
desc = &gq->tx_ring[gq->dirty];
814814
}
815815
}
816816

@@ -1613,6 +1613,8 @@ static netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *nd
16131613
gq->skbs[gq->cur] = skb;
16141614
gq->unmap_addrs[gq->cur] = dma_addr_orig;
16151615

1616+
dma_wmb();
1617+
16161618
/* DT_FSTART should be set at last. So, this is reverse order. */
16171619
for (i = nr_desc; i-- > 0; ) {
16181620
desc = &gq->tx_ring[rswitch_next_queue_index(gq, true, i)];
@@ -1623,8 +1625,6 @@ static netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *nd
16231625
goto err_unmap;
16241626
}
16251627

1626-
wmb(); /* gq->cur must be incremented after die_dt was set */
1627-
16281628
gq->cur = rswitch_next_queue_index(gq, true, nr_desc);
16291629
rswitch_modify(rdev->addr, GWTRC(gq->index), 0, BIT(gq->index % 32));
16301630

0 commit comments

Comments
 (0)