The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes
@ 2026-07-19  5:30 Luke Howard
  2026-07-19  5:30 ` [PATCH net-next v3 1/3] net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages Luke Howard
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Luke Howard @ 2026-07-19  5:30 UTC (permalink / raw)
  To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
	Richard Cochran
  Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
	Christoph Mellauner, Simon Gapp, netdev, linux-kernel,
	Luke Howard

Three fixes for improving the reliably of hardware timestamp acquisition
on Marvell switches. In our tests this eliminated missed timestamps in
ptp4l, whilst also preserving PTP event and general message ordering on
switches that support embedded timestamps.

Note: squashing the two embedded timestamp patches into a single commit
may be desirable.

Signed-off-by: Luke Howard <lukeh@padl.com>
---
Changes in v3:
- Unset UDP checksum on all embedded timestamp cases
- Handle embedded PTP arrival times without worker dispatch
- Link to v2: https://patch.msgid.link/20260710-mv88e6xxx-ptp-fixes-v2-0-af97c38df247@padl.com

Changes in v2:
- Lineraize SKBs before retrieving embedded timestamp
- Hoist driver register lock out of per-frame loop
- Link to v1: https://patch.msgid.link/20260703-mv88e6xxx-ptp-fixes-v1-0-0138581889a9@padl.com

To: Andrew Lunn <andrew@lunn.ch>
To: Vladimir Oltean <olteanv@gmail.com>
To: "David S. Miller" <davem@davemloft.net>
To: Eric Dumazet <edumazet@google.com>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
To: Richard Cochran <richardcochran@gmail.com>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

---
Luke Howard (3):
      net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages
      net: dsa: mv88e6xxx: embedded PTP timestamp support
      net: dsa: mv88e6xxx: apply embedded PTP arrival times inline

 drivers/net/dsa/mv88e6xxx/chip.c     |   4 ++
 drivers/net/dsa/mv88e6xxx/chip.h     |  15 +++-
 drivers/net/dsa/mv88e6xxx/hwtstamp.c | 134 +++++++++++++++++++++++++++++------
 drivers/net/dsa/mv88e6xxx/hwtstamp.h |  14 ++++
 drivers/net/dsa/mv88e6xxx/ptp.c      |  65 ++++++++++++++---
 drivers/net/dsa/mv88e6xxx/ptp.h      |   1 +
 6 files changed, 202 insertions(+), 31 deletions(-)
---
base-commit: f6f3b36c15ed44de1fbb44e645e4fae8c4a4453e
change-id: 20260630-mv88e6xxx-ptp-fixes-1732570b8829

Best regards,
--  
Luke Howard <lukeh@padl.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next v3 1/3] net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages
  2026-07-19  5:30 [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes Luke Howard
@ 2026-07-19  5:30 ` Luke Howard
  2026-07-19  5:30 ` [PATCH net-next v3 2/3] net: dsa: mv88e6xxx: embedded PTP timestamp support Luke Howard
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Luke Howard @ 2026-07-19  5:30 UTC (permalink / raw)
  To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
	Richard Cochran
  Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
	Christoph Mellauner, Simon Gapp, netdev, linux-kernel,
	Luke Howard

mv88e6xxx switches have two arrival timestamp counters for timestamping
PTP event messages. This permits more than one arriving event message's
timestamp to be captured. This is useful for the case where event
messages from a grandmaster arrive at the same time as PDelayReq/PDelayResp
messages from a peer.

Previously only PDelayResp messages were assigned to the second arrival
counter; this patch does so for PDelayReq messages as well.

Signed-off-by: Luke Howard <lukeh@padl.com>
---
 drivers/net/dsa/mv88e6xxx/hwtstamp.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
index 6e6472a3b75ad..57ff77496864f 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
@@ -319,9 +319,16 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip,
 				   &ps->rx_queue2);
 }
 
-static int is_pdelay_resp(const struct ptp_header *hdr)
+static bool is_pdelay_msg(const struct ptp_header *hdr)
 {
-	return (hdr->tsmt & 0xf) == 3;
+	switch (ptp_get_msgtype(hdr, PTP_CLASS_V2)) {
+	case PTP_MSGTYPE_PDELAY_REQ:
+		fallthrough;
+	case PTP_MSGTYPE_PDELAY_RESP:
+		return true;
+	default:
+		return false;
+	}
 }
 
 bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
@@ -343,7 +350,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
 
 	SKB_PTP_TYPE(skb) = type;
 
-	if (is_pdelay_resp(hdr))
+	if (is_pdelay_msg(hdr))
 		skb_queue_tail(&ps->rx_queue2, skb);
 	else
 		skb_queue_tail(&ps->rx_queue, skb);
@@ -584,8 +591,9 @@ int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip)
 	if (err)
 		return err;
 
-	/* Use ARRIVAL1 for peer delay response messages. */
+	/* Use ARRIVAL1 for peer delay messages. */
 	err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR,
+				  MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ |
 				  MV88E6XXX_PTP_MSGTYPE_PDLAY_RES);
 	if (err)
 		return err;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net-next v3 2/3] net: dsa: mv88e6xxx: embedded PTP timestamp support
  2026-07-19  5:30 [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes Luke Howard
  2026-07-19  5:30 ` [PATCH net-next v3 1/3] net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages Luke Howard
@ 2026-07-19  5:30 ` Luke Howard
  2026-07-24 23:48   ` Jakub Kicinski
  2026-07-19  5:30 ` [PATCH net-next v3 3/3] net: dsa: mv88e6xxx: apply embedded PTP arrival times inline Luke Howard
  2026-07-24 23:52 ` [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes Jakub Kicinski
  3 siblings, 1 reply; 6+ messages in thread
From: Luke Howard @ 2026-07-19  5:30 UTC (permalink / raw)
  To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
	Richard Cochran
  Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
	Christoph Mellauner, Simon Gapp, netdev, linux-kernel,
	Luke Howard

mv88e6xxx switches can support embedding PTP timestamps directly
in the frame, either as a trailer or at a configurable offset
(typically the reserved bytes in the PTP header).

Add support for this on the 88E6341 and 88E6352 switches, being
those on which I was able to verify this. Other switch chips may
also work. The arrival timestamp offsets are relative to the PTP
common header and will work for both L2 and L3 PTP packets; the
respective headers are skipped by the number of bytes set in the
ETJump and IPJump registers, which are initialized to sensible
defaults and are VLAN tag-aware.

(Note: the 6352 datasheet incorrectly states that ETJump and
IPJump are initialized to zero. They are initialized to 12 and
2 respectively; this is corrected in the 6341 data sheet.)

Signed-off-by: Luke Howard <lukeh@padl.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c     |   3 +
 drivers/net/dsa/mv88e6xxx/chip.h     |   8 +++
 drivers/net/dsa/mv88e6xxx/hwtstamp.c | 131 +++++++++++++++++++++++++++++++----
 drivers/net/dsa/mv88e6xxx/hwtstamp.h |  14 ++++
 4 files changed, 144 insertions(+), 12 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 80b877c74513d..c3277c1f3d785 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -28,6 +28,7 @@
 #include <linux/of_mdio.h>
 #include <linux/platform_data/mv88e6xxx.h>
 #include <linux/property.h>
+#include <linux/ptp_classify.h>
 #include <linux/netdevice.h>
 #include <linux/gpio/consumer.h>
 #include <linux/phylink.h>
@@ -6370,6 +6371,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
 		.ptp_support = true,
 		.ops = &mv88e6341_ops,
+		.arr_ts_mode = offsetof(struct ptp_header, reserved2),
 	},
 
 	[MV88E6350] = {
@@ -6447,6 +6449,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
 		.ptp_support = true,
 		.ops = &mv88e6352_ops,
+		.arr_ts_mode = offsetof(struct ptp_header, reserved2),
 	},
 	[MV88E6361] = {
 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6361,
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index e966e7c4cc5de..b6a90eba81c43 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -180,6 +180,14 @@ struct mv88e6xxx_info {
 	 * port 0, 1 means internal PHYs range starts at port 1, etc
 	 */
 	unsigned int internal_phys_offset;
+
+	/* Arrival Time Stamp Mode (ArrTSMode); see the ArrTSMode encoding in
+	 * hwtstamp.h. Zero (the default) leaves arrival time stamps in the
+	 * switch registers; non-zero embeds them in the frame, either appended
+	 * as a trailer or overwritten at that byte offset past the start of the
+	 * PTP common header.
+	 */
+	unsigned int arr_ts_mode;
 };
 
 struct mv88e6xxx_atu_entry {
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
index 57ff77496864f..fa5e897182ca2 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
@@ -15,6 +15,7 @@
 #include "hwtstamp.h"
 #include "ptp.h"
 #include <linux/ptp_classify.h>
+#include <linux/unaligned.h>
 
 #define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb))
 
@@ -245,6 +246,74 @@ static int seq_match(struct sk_buff *skb, u16 ts_seqid)
 	return ts_seqid == ntohs(hdr->sequence_id);
 }
 
+static bool parse_embedded_ts(unsigned int arr_ts_mode,
+			      struct sk_buff *skb, u64 *ns)
+{
+	struct ptp_header *hdr;
+
+	*ns = 0;
+
+	/* APPEND means the switch appended the time stamp as a 4-byte trailer
+	 * (not all switches support this). Any other non-zero value is the byte
+	 * offset past the start of the PTP common header at which the switch
+	 * overwrote the time stamp in place (e.g. the reserved header bytes).
+	 */
+	if (arr_ts_mode == MV88E6XXX_PTP_ARR_TS_MODE_APPEND && skb->len >= 4) {
+		if (skb_linearize(skb))
+			return false;
+
+		*ns = (u64)get_unaligned_be32(skb_tail_pointer(skb) - 4);
+		if (pskb_trim_rcsum(skb, skb->len - 4))
+			return false;
+	} else if (arr_ts_mode + 4 <= sizeof(*hdr)) {
+		if (skb_linearize(skb))
+			return false;
+
+		hdr = ptp_parse_header(skb, SKB_PTP_TYPE(skb));
+		if (!hdr)
+			return false;
+
+		*ns = (u64)get_unaligned_be32((u8 *)hdr + arr_ts_mode);
+		memset((u8 *)hdr + arr_ts_mode, 0, 4);
+		skb_checksum_complete_unset(skb);
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+static void mv88e6xxx_get_rxts_embedded(struct mv88e6xxx_chip *chip,
+					struct mv88e6xxx_port_hwtstamp *ps,
+					struct sk_buff *skb)
+{
+	struct sk_buff_head *rxq = &ps->rx_queue;
+	struct skb_shared_hwtstamps *shwt;
+	struct sk_buff_head received;
+	unsigned long flags;
+	u64 ns;
+
+	__skb_queue_head_init(&received);
+	__skb_queue_head(&received, skb);
+	spin_lock_irqsave(&rxq->lock, flags);
+	skb_queue_splice_tail_init(rxq, &received);
+	spin_unlock_irqrestore(&rxq->lock, flags);
+
+	mv88e6xxx_reg_lock(chip);
+	skb_queue_walk(&received, skb) {
+		if (!parse_embedded_ts(chip->info->arr_ts_mode, skb, &ns))
+			continue;
+		ns = timecounter_cyc2time(&chip->tstamp_tc, ns);
+		shwt = skb_hwtstamps(skb);
+		memset(shwt, 0, sizeof(*shwt));
+		shwt->hwtstamp = ns_to_ktime(ns);
+	}
+	mv88e6xxx_reg_unlock(chip);
+
+	while ((skb = __skb_dequeue(&received)))
+		netif_rx(skb);
+}
+
 static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip,
 			       struct mv88e6xxx_port_hwtstamp *ps,
 			       struct sk_buff *skb, u16 reg,
@@ -307,8 +376,21 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip,
 	const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
 	struct sk_buff *skb;
 
-	skb = skb_dequeue(&ps->rx_queue);
+	if (chip->info->arr_ts_mode) {
+		/* If arr_ts_mode is set, the timestamps are embedded in the
+		 * frames so a register read is not required. We still need a
+		 * work queue rather than processing inline because
+		 * timecounter_cyc2time() takes the global mutex and this
+		 * cannot be called from mv88e6xxx_port_rxtstamp().
+		 */
+		skb = skb_dequeue(&ps->rx_queue);
+		if (skb)
+			mv88e6xxx_get_rxts_embedded(chip, ps, skb);
 
+		return;
+	}
+
+	skb = skb_dequeue(&ps->rx_queue);
 	if (skb)
 		mv88e6xxx_get_rxts(chip, ps, skb, ptp_ops->arr0_sts_reg,
 				   &ps->rx_queue);
@@ -350,7 +432,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
 
 	SKB_PTP_TYPE(skb) = type;
 
-	if (is_pdelay_msg(hdr))
+	if (!chip->info->arr_ts_mode && is_pdelay_msg(hdr))
 		skb_queue_tail(&ps->rx_queue2, skb);
 	else
 		skb_queue_tail(&ps->rx_queue, skb);
@@ -530,14 +612,37 @@ int mv88e6165_global_enable(struct mv88e6xxx_chip *chip)
 
 int mv88e6352_hwtstamp_port_disable(struct mv88e6xxx_chip *chip, int port)
 {
-	return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
-					MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP);
+	int err;
+
+	err = mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
+				       MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP);
+	if (err)
+		return err;
+
+	err = mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG2, 0);
+	if (err)
+		return err;
+
+	return 0;
 }
 
 int mv88e6352_hwtstamp_port_enable(struct mv88e6xxx_chip *chip, int port)
 {
-	return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
-					MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH);
+	int err;
+
+	if (chip->info->arr_ts_mode) {
+		err = mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG2,
+					       chip->info->arr_ts_mode << 8);
+		if (err)
+			return err;
+	}
+
+	err = mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
+				       MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH);
+	if (err)
+		return err;
+
+	return 0;
 }
 
 static int mv88e6xxx_hwtstamp_port_setup(struct mv88e6xxx_chip *chip, int port)
@@ -591,12 +696,14 @@ int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip)
 	if (err)
 		return err;
 
-	/* Use ARRIVAL1 for peer delay messages. */
-	err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR,
-				  MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ |
-				  MV88E6XXX_PTP_MSGTYPE_PDLAY_RES);
-	if (err)
-		return err;
+	if (!chip->info->arr_ts_mode) {
+		/* Use ARRIVAL1 for peer delay messages. */
+		err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR,
+					  MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ |
+					  MV88E6XXX_PTP_MSGTYPE_PDLAY_RES);
+		if (err)
+			return err;
+	}
 
 	/* 88E6341 devices default to timestamping at the PHY, but this has
 	 * a hardware issue that results in unreliable timestamps. Force
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.h b/drivers/net/dsa/mv88e6xxx/hwtstamp.h
index c359821d5a6ea..c25f53923e768 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.h
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.h
@@ -68,6 +68,20 @@
 #define MV88E6XXX_PORT_PTP_CFG2_DEP_IRQ_EN		0x0002
 #define MV88E6XXX_PORT_PTP_CFG2_ARR_IRQ_EN		0x0001
 
+/* Arrival Time Stamp Mode (ArrTSMode), CFG2 bits [15:8]: configures how the
+ * switch embeds the arrival time stamp (PTPArr0Time) into enabled PTP event
+ * frames.
+ *   0x00        frame modification disabled (time stamp read from registers)
+ *   0x01        append the 4-byte time stamp at the end of the frame,
+ *               growing the frame by four bytes
+ *   0x04..0xEF  overwrite the 4-byte time stamp in place, that many bytes past
+ *               the start of the PTP common header, without growing the frame
+ *               (offsetof(struct ptp_header, reserved2) targets the reserved
+ *               bytes of the header)
+ *   others      reserved
+ */
+#define MV88E6XXX_PTP_ARR_TS_MODE_APPEND		0x01
+
 /* Offset 0x03: PTP LED Configuration */
 #define MV88E6XXX_PORT_PTP_LED_CFG	0x03
 

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net-next v3 3/3] net: dsa: mv88e6xxx: apply embedded PTP arrival times inline
  2026-07-19  5:30 [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes Luke Howard
  2026-07-19  5:30 ` [PATCH net-next v3 1/3] net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages Luke Howard
  2026-07-19  5:30 ` [PATCH net-next v3 2/3] net: dsa: mv88e6xxx: embedded PTP timestamp support Luke Howard
@ 2026-07-19  5:30 ` Luke Howard
  2026-07-24 23:52 ` [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes Jakub Kicinski
  3 siblings, 0 replies; 6+ messages in thread
From: Luke Howard @ 2026-07-19  5:30 UTC (permalink / raw)
  To: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Andrew Lunn,
	Richard Cochran
  Cc: Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
	Christoph Mellauner, Simon Gapp, netdev, linux-kernel,
	Luke Howard

Embedded PTP arrival times can be extracted without using the PTP
worker, avoiding the issue where PTP general messages could arrive
before the timestamped event messages. DSA can deliver the frame
normally, similar to ocelot_ptp_rx_timestamp().

The register-based arrival path is unchanged.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Luke Howard <lukeh@padl.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c     |  1 +
 drivers/net/dsa/mv88e6xxx/chip.h     |  7 ++--
 drivers/net/dsa/mv88e6xxx/hwtstamp.c | 69 +++++++++++++-----------------------
 drivers/net/dsa/mv88e6xxx/ptp.c      | 65 ++++++++++++++++++++++++++++-----
 drivers/net/dsa/mv88e6xxx/ptp.h      |  1 +
 5 files changed, 87 insertions(+), 56 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index c3277c1f3d785..624cdf6c80a67 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -6638,6 +6638,7 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
 	chip->dev = dev;
 
 	mutex_init(&chip->reg_lock);
+	spin_lock_init(&chip->ptp_clock_lock);
 	INIT_LIST_HEAD(&chip->mdios);
 	idr_init(&chip->policies);
 	INIT_LIST_HEAD(&chip->msts);
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index b6a90eba81c43..dd52de71360b9 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -427,9 +427,12 @@ struct mv88e6xxx_chip {
 	/* GPIO resources */
 	u8 gpio_data[2];
 
-	/* This cyclecounter abstracts the switch PTP time.
-	 * reg_lock must be held for any operation that read()s.
+	/* This cyclecounter abstracts the switch PTP time. ptp_clock_lock
+	 * protects tstamp_cc and tstamp_tc. tstamp_cycles caches the
+	 * result most recently returned by mv88e6xxx_ptp_read_cycles().
 	 */
+	spinlock_t		ptp_clock_lock;
+	u64			tstamp_cycles;
 	struct cyclecounter	tstamp_cc;
 	struct timecounter	tstamp_tc;
 	struct delayed_work	overflow_work;
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
index fa5e897182ca2..716cbc2a86922 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
@@ -283,35 +283,24 @@ static bool parse_embedded_ts(unsigned int arr_ts_mode,
 	return true;
 }
 
-static void mv88e6xxx_get_rxts_embedded(struct mv88e6xxx_chip *chip,
-					struct mv88e6xxx_port_hwtstamp *ps,
-					struct sk_buff *skb)
+/* Apply the arrival time the switch embedded in the frame. No register access
+ * is needed, so this runs inline on the receive path rather than being handed
+ * to the PTP worker.
+ */
+static void mv88e6xxx_ptp_rx_timestamp(struct mv88e6xxx_chip *chip,
+				       struct sk_buff *skb)
 {
-	struct sk_buff_head *rxq = &ps->rx_queue;
 	struct skb_shared_hwtstamps *shwt;
-	struct sk_buff_head received;
-	unsigned long flags;
 	u64 ns;
 
-	__skb_queue_head_init(&received);
-	__skb_queue_head(&received, skb);
-	spin_lock_irqsave(&rxq->lock, flags);
-	skb_queue_splice_tail_init(rxq, &received);
-	spin_unlock_irqrestore(&rxq->lock, flags);
+	if (!parse_embedded_ts(chip->info->arr_ts_mode, skb, &ns))
+		return;
 
-	mv88e6xxx_reg_lock(chip);
-	skb_queue_walk(&received, skb) {
-		if (!parse_embedded_ts(chip->info->arr_ts_mode, skb, &ns))
-			continue;
-		ns = timecounter_cyc2time(&chip->tstamp_tc, ns);
-		shwt = skb_hwtstamps(skb);
-		memset(shwt, 0, sizeof(*shwt));
-		shwt->hwtstamp = ns_to_ktime(ns);
-	}
-	mv88e6xxx_reg_unlock(chip);
+	ns = mv88e6xxx_timecounter_cyc2time(chip, ns);
 
-	while ((skb = __skb_dequeue(&received)))
-		netif_rx(skb);
+	shwt = skb_hwtstamps(skb);
+	memset(shwt, 0, sizeof(*shwt));
+	shwt->hwtstamp = ns_to_ktime(ns);
 }
 
 static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip,
@@ -358,9 +347,7 @@ static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip,
 		if (mv88e6xxx_ts_valid(status) && seq_match(skb, seq_id)) {
 			ns = timehi << 16 | timelo;
 
-			mv88e6xxx_reg_lock(chip);
-			ns = timecounter_cyc2time(&chip->tstamp_tc, ns);
-			mv88e6xxx_reg_unlock(chip);
+			ns = mv88e6xxx_timecounter_cyc2time(chip, ns);
 			shwt = skb_hwtstamps(skb);
 			memset(shwt, 0, sizeof(*shwt));
 			shwt->hwtstamp = ns_to_ktime(ns);
@@ -376,20 +363,6 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip,
 	const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
 	struct sk_buff *skb;
 
-	if (chip->info->arr_ts_mode) {
-		/* If arr_ts_mode is set, the timestamps are embedded in the
-		 * frames so a register read is not required. We still need a
-		 * work queue rather than processing inline because
-		 * timecounter_cyc2time() takes the global mutex and this
-		 * cannot be called from mv88e6xxx_port_rxtstamp().
-		 */
-		skb = skb_dequeue(&ps->rx_queue);
-		if (skb)
-			mv88e6xxx_get_rxts_embedded(chip, ps, skb);
-
-		return;
-	}
-
 	skb = skb_dequeue(&ps->rx_queue);
 	if (skb)
 		mv88e6xxx_get_rxts(chip, ps, skb, ptp_ops->arr0_sts_reg,
@@ -432,7 +405,13 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
 
 	SKB_PTP_TYPE(skb) = type;
 
-	if (!chip->info->arr_ts_mode && is_pdelay_msg(hdr))
+	/* Embedded arrival times can be returned inline. */
+	if (chip->info->arr_ts_mode) {
+		mv88e6xxx_ptp_rx_timestamp(chip, skb);
+		return false;
+	}
+
+	if (is_pdelay_msg(hdr))
 		skb_queue_tail(&ps->rx_queue2, skb);
 	else
 		skb_queue_tail(&ps->rx_queue, skb);
@@ -498,9 +477,7 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip,
 
 	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
 	time_raw = ((u32)departure_block[2] << 16) | departure_block[1];
-	mv88e6xxx_reg_lock(chip);
-	ns = timecounter_cyc2time(&chip->tstamp_tc, time_raw);
-	mv88e6xxx_reg_unlock(chip);
+	ns = mv88e6xxx_timecounter_cyc2time(chip, time_raw);
 	shhwtstamps.hwtstamp = ns_to_ktime(ns);
 
 	dev_dbg(chip->dev,
@@ -543,7 +520,9 @@ long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp)
 		if (test_bit(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state))
 			restart |= mv88e6xxx_txtstamp_work(chip, ps);
 
-		mv88e6xxx_rxtstamp_work(chip, ps);
+		/* Embedded arrival times are applied on the receive path. */
+		if (!chip->info->arr_ts_mode)
+			mv88e6xxx_rxtstamp_work(chip, ps);
 	}
 
 	return restart ? 1 : -1;
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c
index f7603573d3a98..2a7f97625e375 100644
--- a/drivers/net/dsa/mv88e6xxx/ptp.c
+++ b/drivers/net/dsa/mv88e6xxx/ptp.c
@@ -231,15 +231,37 @@ static void mv88e6352_tai_event_work(struct work_struct *ugly)
 
 	/* We only have one timestamping channel. */
 	ev.index = 0;
-	mv88e6xxx_reg_lock(chip);
-	ev.timestamp = timecounter_cyc2time(&chip->tstamp_tc, raw_ts);
-	mv88e6xxx_reg_unlock(chip);
+	ev.timestamp = mv88e6xxx_timecounter_cyc2time(chip, raw_ts);
 
 	ptp_clock_event(chip->ptp_clock, &ev);
 out:
 	schedule_delayed_work(&chip->tai_event_work, TAI_EVENT_WORK_INTERVAL);
 }
 
+/* Refresh the cached counter value that the read() callback returns. The
+ * read cannot acquire the register lock whilst holding ptp_clock_lock
+ * because MDIO reads can sleep. The caller must hold reg_lock, which
+ * serializes against the other timecounter writers.
+ */
+static void mv88e6xxx_ptp_read_cycles(struct mv88e6xxx_chip *chip)
+{
+	const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
+
+	if (ptp_ops->clock_read)
+		chip->tstamp_cycles = ptp_ops->clock_read(&chip->tstamp_cc);
+}
+
+u64 mv88e6xxx_timecounter_cyc2time(struct mv88e6xxx_chip *chip, u64 cycles)
+{
+	u64 ns;
+
+	spin_lock_bh(&chip->ptp_clock_lock);
+	ns = timecounter_cyc2time(&chip->tstamp_tc, cycles);
+	spin_unlock_bh(&chip->ptp_clock_lock);
+
+	return ns;
+}
+
 static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
 {
 	struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
@@ -258,9 +280,12 @@ static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
 	diff = div_u64(adj, chip->cc_coeffs->cc_mult_dem);
 
 	mv88e6xxx_reg_lock(chip);
+	mv88e6xxx_ptp_read_cycles(chip);
 
+	spin_lock_bh(&chip->ptp_clock_lock);
 	timecounter_read(&chip->tstamp_tc);
 	chip->tstamp_cc.mult = neg_adj ? mult - diff : mult + diff;
+	spin_unlock_bh(&chip->ptp_clock_lock);
 
 	mv88e6xxx_reg_unlock(chip);
 
@@ -271,8 +296,16 @@ static int mv88e6xxx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
 {
 	struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
 
+	/* No register access is needed here, but reg_lock still serialises
+	 * this against the other timecounter writers, which drop it only
+	 * after their hardware read has completed.
+	 */
 	mv88e6xxx_reg_lock(chip);
+
+	spin_lock_bh(&chip->ptp_clock_lock);
 	timecounter_adjtime(&chip->tstamp_tc, delta);
+	spin_unlock_bh(&chip->ptp_clock_lock);
+
 	mv88e6xxx_reg_unlock(chip);
 
 	return 0;
@@ -285,7 +318,12 @@ static int mv88e6xxx_ptp_gettime(struct ptp_clock_info *ptp,
 	u64 ns;
 
 	mv88e6xxx_reg_lock(chip);
+	mv88e6xxx_ptp_read_cycles(chip);
+
+	spin_lock_bh(&chip->ptp_clock_lock);
 	ns = timecounter_read(&chip->tstamp_tc);
+	spin_unlock_bh(&chip->ptp_clock_lock);
+
 	mv88e6xxx_reg_unlock(chip);
 
 	*ts = ns_to_timespec64(ns);
@@ -302,7 +340,12 @@ static int mv88e6xxx_ptp_settime(struct ptp_clock_info *ptp,
 	ns = timespec64_to_ns(ts);
 
 	mv88e6xxx_reg_lock(chip);
+	mv88e6xxx_ptp_read_cycles(chip);
+
+	spin_lock_bh(&chip->ptp_clock_lock);
 	timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, ns);
+	spin_unlock_bh(&chip->ptp_clock_lock);
+
 	mv88e6xxx_reg_unlock(chip);
 
 	return 0;
@@ -444,14 +487,12 @@ const struct mv88e6xxx_ptp_ops mv88e6390_ptp_ops = {
 		(1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ),
 };
 
+/* Return the value most recently fetched by mv88e6xxx_ptp_read_cycles()
+ * rather than reading the hardware over MDIO.
+ */
 static u64 mv88e6xxx_ptp_clock_read(struct cyclecounter *cc)
 {
-	struct mv88e6xxx_chip *chip = cc_to_chip(cc);
-
-	if (chip->info->ops->ptp_ops->clock_read)
-		return chip->info->ops->ptp_ops->clock_read(cc);
-
-	return 0;
+	return cc_to_chip(cc)->tstamp_cycles;
 }
 
 /* With a 250MHz input clock, the 32-bit timestamp counter overflows in ~17.2
@@ -486,6 +527,12 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
 	chip->tstamp_cc.mult	= chip->cc_coeffs->cc_mult;
 	chip->tstamp_cc.shift	= chip->cc_coeffs->cc_shift;
 
+	/* Prime the cycle counter cache for the timecounter_init() below.
+	 * The caller holds reg_lock, and nothing can reach the PTP clock
+	 * until ptp_clock_register() below, so no locking is needed here.
+	 */
+	mv88e6xxx_ptp_read_cycles(chip);
+
 	timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc,
 			 ktime_to_ns(ktime_get_real()));
 
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/ptp.h
index 95bdddb0bf39f..44718e120de20 100644
--- a/drivers/net/dsa/mv88e6xxx/ptp.h
+++ b/drivers/net/dsa/mv88e6xxx/ptp.h
@@ -68,6 +68,7 @@
 
 int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip);
 void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip);
+u64 mv88e6xxx_timecounter_cyc2time(struct mv88e6xxx_chip *chip, u64 cycles);
 
 #define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip,	\
 				      ptp_clock_info)

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v3 2/3] net: dsa: mv88e6xxx: embedded PTP timestamp support
  2026-07-19  5:30 ` [PATCH net-next v3 2/3] net: dsa: mv88e6xxx: embedded PTP timestamp support Luke Howard
@ 2026-07-24 23:48   ` Jakub Kicinski
  0 siblings, 0 replies; 6+ messages in thread
From: Jakub Kicinski @ 2026-07-24 23:48 UTC (permalink / raw)
  To: Luke Howard
  Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Paolo Abeni,
	Vivien Didelot, Gregory CLEMENT, Andrew Lunn, Richard Cochran,
	Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
	Christoph Mellauner, Simon Gapp, netdev, linux-kernel

On Sun, 19 Jul 2026 15:30:54 +1000 Luke Howard wrote:
> +	/* APPEND means the switch appended the time stamp as a 4-byte trailer
> +	 * (not all switches support this). Any other non-zero value is the byte
> +	 * offset past the start of the PTP common header at which the switch
> +	 * overwrote the time stamp in place (e.g. the reserved header bytes).
> +	 */
> +	if (arr_ts_mode == MV88E6XXX_PTP_ARR_TS_MODE_APPEND && skb->len >= 4) {
> +		if (skb_linearize(skb))
> +			return false;
> +
> +		*ns = (u64)get_unaligned_be32(skb_tail_pointer(skb) - 4);
> +		if (pskb_trim_rcsum(skb, skb->len - 4))
> +			return false;

The append mode would extend the frame, and possibly run afoul the MTU
on the conduit AFAIU? Since neither implemented chip uses this it's
both dead code and questionable. Let's remove it.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes
  2026-07-19  5:30 [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes Luke Howard
                   ` (2 preceding siblings ...)
  2026-07-19  5:30 ` [PATCH net-next v3 3/3] net: dsa: mv88e6xxx: apply embedded PTP arrival times inline Luke Howard
@ 2026-07-24 23:52 ` Jakub Kicinski
  3 siblings, 0 replies; 6+ messages in thread
From: Jakub Kicinski @ 2026-07-24 23:52 UTC (permalink / raw)
  To: Luke Howard, Andrew Lunn, Richard Cochran
  Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Paolo Abeni,
	Vivien Didelot, Gregory CLEMENT, Cedric Jehasse, Kieran Tyrrell,
	Max Holtmann, Max Hunter, Christoph Mellauner, Simon Gapp, netdev,
	linux-kernel

On Sun, 19 Jul 2026 15:30:52 +1000 Luke Howard wrote:
> Three fixes for improving the reliably of hardware timestamp acquisition
> on Marvell switches. In our tests this eliminated missed timestamps in
> ptp4l, whilst also preserving PTP event and general message ordering on
> switches that support embedded timestamps.

Do you know if there's any precedent in the existing upstream drivers
for overriding the reserved bytes? It's not normal for devices to modify
the ingress frames. Perhaps there's some precedent, but if not I think
we should gate this (eg with a devlink param?)

Andrew, do you have any opinion? Or perhaps Richard?

> Note: squashing the two embedded timestamp patches into a single commit
> may be desirable.

I think it would be great to invert the order. Revamp the locking as
patch 2 then add the new feature as patch 3.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-07-24 23:52 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-19  5:30 [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes Luke Howard
2026-07-19  5:30 ` [PATCH net-next v3 1/3] net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages Luke Howard
2026-07-19  5:30 ` [PATCH net-next v3 2/3] net: dsa: mv88e6xxx: embedded PTP timestamp support Luke Howard
2026-07-24 23:48   ` Jakub Kicinski
2026-07-19  5:30 ` [PATCH net-next v3 3/3] net: dsa: mv88e6xxx: apply embedded PTP arrival times inline Luke Howard
2026-07-24 23:52 ` [PATCH net-next v3 0/3] net: dsa: mv88e6xxx: various hwstamp fixes Jakub Kicinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox