Skip to content

Commit 922b4b9

Browse files
nikita-youshkuba-moo
authored andcommitted
net: renesas: rswitch: rework ts tags management
The existing linked list based implementation of how ts tags are assigned and managed is unsafe against concurrency and corner cases: - element addition in tx processing can race against element removal in ts queue completion, - element removal in ts queue completion can race against element removal in device close, - if a large number of frames gets added to tx queue without ts queue completions in between, elements with duplicate tag values can get added. Use a different implementation, based on per-port used tags bitmaps and saved skb arrays. Safety for addition in tx processing vs removal in ts completion is provided by: tag = find_first_zero_bit(...); smp_mb(); <write rdev->ts_skb[tag]> set_bit(...); vs <read rdev->ts_skb[tag]> smp_mb(); clear_bit(...); Safety for removal in ts completion vs removal in device close is provided by using atomic read-and-clear for rdev->ts_skb[tag]: ts_skb = xchg(&rdev->ts_skb[tag], NULL); if (ts_skb) <handle it> Fixes: 33f5d73 ("net: renesas: rswitch: Improve TX timestamp accuracy") Signed-off-by: Nikita Yushchenko <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent cb85f2b commit 922b4b9

File tree

2 files changed

+42
-45
lines changed

2 files changed

+42
-45
lines changed

drivers/net/ethernet/renesas/rswitch.c

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,6 @@ static int rswitch_gwca_ts_queue_alloc(struct rswitch_private *priv)
547547
desc = &gq->ts_ring[gq->ring_size];
548548
desc->desc.die_dt = DT_LINKFIX;
549549
rswitch_desc_set_dptr(&desc->desc, gq->ring_dma);
550-
INIT_LIST_HEAD(&priv->gwca.ts_info_list);
551550

552551
return 0;
553552
}
@@ -1003,9 +1002,10 @@ static int rswitch_gwca_request_irqs(struct rswitch_private *priv)
10031002
static void rswitch_ts(struct rswitch_private *priv)
10041003
{
10051004
struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue;
1006-
struct rswitch_gwca_ts_info *ts_info, *ts_info2;
10071005
struct skb_shared_hwtstamps shhwtstamps;
10081006
struct rswitch_ts_desc *desc;
1007+
struct rswitch_device *rdev;
1008+
struct sk_buff *ts_skb;
10091009
struct timespec64 ts;
10101010
unsigned int num;
10111011
u32 tag, port;
@@ -1015,23 +1015,28 @@ static void rswitch_ts(struct rswitch_private *priv)
10151015
dma_rmb();
10161016

10171017
port = TS_DESC_DPN(__le32_to_cpu(desc->desc.dptrl));
1018-
tag = TS_DESC_TSUN(__le32_to_cpu(desc->desc.dptrl));
1019-
1020-
list_for_each_entry_safe(ts_info, ts_info2, &priv->gwca.ts_info_list, list) {
1021-
if (!(ts_info->port == port && ts_info->tag == tag))
1022-
continue;
1023-
1024-
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
1025-
ts.tv_sec = __le32_to_cpu(desc->ts_sec);
1026-
ts.tv_nsec = __le32_to_cpu(desc->ts_nsec & cpu_to_le32(0x3fffffff));
1027-
shhwtstamps.hwtstamp = timespec64_to_ktime(ts);
1028-
skb_tstamp_tx(ts_info->skb, &shhwtstamps);
1029-
dev_consume_skb_irq(ts_info->skb);
1030-
list_del(&ts_info->list);
1031-
kfree(ts_info);
1032-
break;
1033-
}
1018+
if (unlikely(port >= RSWITCH_NUM_PORTS))
1019+
goto next;
1020+
rdev = priv->rdev[port];
10341021

1022+
tag = TS_DESC_TSUN(__le32_to_cpu(desc->desc.dptrl));
1023+
if (unlikely(tag >= TS_TAGS_PER_PORT))
1024+
goto next;
1025+
ts_skb = xchg(&rdev->ts_skb[tag], NULL);
1026+
smp_mb(); /* order rdev->ts_skb[] read before bitmap update */
1027+
clear_bit(tag, rdev->ts_skb_used);
1028+
1029+
if (unlikely(!ts_skb))
1030+
goto next;
1031+
1032+
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
1033+
ts.tv_sec = __le32_to_cpu(desc->ts_sec);
1034+
ts.tv_nsec = __le32_to_cpu(desc->ts_nsec & cpu_to_le32(0x3fffffff));
1035+
shhwtstamps.hwtstamp = timespec64_to_ktime(ts);
1036+
skb_tstamp_tx(ts_skb, &shhwtstamps);
1037+
dev_consume_skb_irq(ts_skb);
1038+
1039+
next:
10351040
gq->cur = rswitch_next_queue_index(gq, true, 1);
10361041
desc = &gq->ts_ring[gq->cur];
10371042
}
@@ -1576,8 +1581,9 @@ static int rswitch_open(struct net_device *ndev)
15761581
static int rswitch_stop(struct net_device *ndev)
15771582
{
15781583
struct rswitch_device *rdev = netdev_priv(ndev);
1579-
struct rswitch_gwca_ts_info *ts_info, *ts_info2;
1584+
struct sk_buff *ts_skb;
15801585
unsigned long flags;
1586+
unsigned int tag;
15811587

15821588
netif_tx_stop_all_queues(ndev);
15831589

@@ -1594,12 +1600,13 @@ static int rswitch_stop(struct net_device *ndev)
15941600
if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS))
15951601
iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDID);
15961602

1597-
list_for_each_entry_safe(ts_info, ts_info2, &rdev->priv->gwca.ts_info_list, list) {
1598-
if (ts_info->port != rdev->port)
1599-
continue;
1600-
dev_kfree_skb_irq(ts_info->skb);
1601-
list_del(&ts_info->list);
1602-
kfree(ts_info);
1603+
for (tag = find_first_bit(rdev->ts_skb_used, TS_TAGS_PER_PORT);
1604+
tag < TS_TAGS_PER_PORT;
1605+
tag = find_next_bit(rdev->ts_skb_used, TS_TAGS_PER_PORT, tag + 1)) {
1606+
ts_skb = xchg(&rdev->ts_skb[tag], NULL);
1607+
clear_bit(tag, rdev->ts_skb_used);
1608+
if (ts_skb)
1609+
dev_kfree_skb(ts_skb);
16031610
}
16041611

16051612
return 0;
@@ -1612,20 +1619,17 @@ static bool rswitch_ext_desc_set_info1(struct rswitch_device *rdev,
16121619
desc->info1 = cpu_to_le64(INFO1_DV(BIT(rdev->etha->index)) |
16131620
INFO1_IPV(GWCA_IPV_NUM) | INFO1_FMT);
16141621
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
1615-
struct rswitch_gwca_ts_info *ts_info;
1622+
unsigned int tag;
16161623

1617-
ts_info = kzalloc(sizeof(*ts_info), GFP_ATOMIC);
1618-
if (!ts_info)
1624+
tag = find_first_zero_bit(rdev->ts_skb_used, TS_TAGS_PER_PORT);
1625+
if (tag == TS_TAGS_PER_PORT)
16191626
return false;
1627+
smp_mb(); /* order bitmap read before rdev->ts_skb[] write */
1628+
rdev->ts_skb[tag] = skb_get(skb);
1629+
set_bit(tag, rdev->ts_skb_used);
16201630

16211631
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
1622-
rdev->ts_tag++;
1623-
desc->info1 |= cpu_to_le64(INFO1_TSUN(rdev->ts_tag) | INFO1_TXC);
1624-
1625-
ts_info->skb = skb_get(skb);
1626-
ts_info->port = rdev->port;
1627-
ts_info->tag = rdev->ts_tag;
1628-
list_add_tail(&ts_info->list, &rdev->priv->gwca.ts_info_list);
1632+
desc->info1 |= cpu_to_le64(INFO1_TSUN(tag) | INFO1_TXC);
16291633

16301634
skb_tx_timestamp(skb);
16311635
}

drivers/net/ethernet/renesas/rswitch.h

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -972,14 +972,6 @@ struct rswitch_gwca_queue {
972972
};
973973
};
974974

975-
struct rswitch_gwca_ts_info {
976-
struct sk_buff *skb;
977-
struct list_head list;
978-
979-
int port;
980-
u8 tag;
981-
};
982-
983975
#define RSWITCH_NUM_IRQ_REGS (RSWITCH_MAX_NUM_QUEUES / BITS_PER_TYPE(u32))
984976
struct rswitch_gwca {
985977
unsigned int index;
@@ -989,22 +981,23 @@ struct rswitch_gwca {
989981
struct rswitch_gwca_queue *queues;
990982
int num_queues;
991983
struct rswitch_gwca_queue ts_queue;
992-
struct list_head ts_info_list;
993984
DECLARE_BITMAP(used, RSWITCH_MAX_NUM_QUEUES);
994985
u32 tx_irq_bits[RSWITCH_NUM_IRQ_REGS];
995986
u32 rx_irq_bits[RSWITCH_NUM_IRQ_REGS];
996987
int speed;
997988
};
998989

999990
#define NUM_QUEUES_PER_NDEV 2
991+
#define TS_TAGS_PER_PORT 256
1000992
struct rswitch_device {
1001993
struct rswitch_private *priv;
1002994
struct net_device *ndev;
1003995
struct napi_struct napi;
1004996
void __iomem *addr;
1005997
struct rswitch_gwca_queue *tx_queue;
1006998
struct rswitch_gwca_queue *rx_queue;
1007-
u8 ts_tag;
999+
struct sk_buff *ts_skb[TS_TAGS_PER_PORT];
1000+
DECLARE_BITMAP(ts_skb_used, TS_TAGS_PER_PORT);
10081001
bool disabled;
10091002

10101003
int port;

0 commit comments

Comments
 (0)