KSZ9563/KSZ8563 and LAN937x switch are capable for supporting IEEE 1588 PTP
protocol. LAN937x has the same PTP register set similar to KSZ9563, hence the
implementation has been made common for the KSZ switches. KSZ9563 does not
support two step timestamping but LAN937x supports both. Tested the 1step &
2step p2p timestamping in LAN937x and p2p1step timestamping in KSZ9563.
This patch series is based on the Christian Eggers PTP support for KSZ9563.
Applied the Christian patch and updated as per the latest refactoring of KSZ
series code. The features added on top are PTP packet Interrupt
implementation based on nested handler, LAN937x two step timestamping and
programmable per_out pins.
Link: https://www.spinics.net/lists/netdev/msg705531.html
Patch v6 -> v7
- Corrected the misplaced spaces and tabs
- Added mutex lock in do_aux_work
- Replaced 0/1 with false/true for ts_en
- SKB_TX_INPROGRESS flag is set before dsa_enqueue_skb
- Removed the fallthrough keyword
- pdelay_resp header correction is performed based on
KSZ_SKB_CB(skb)->update_correction instead of clone
Patch v5 -> v6
- Rebased to latest net-next and renamed from RFC to patch net-next.
Patch v4 -> v5
- Replaced irq_domain_add_simple with irq_doamin_add_linear
- Used the helper diff_by_scaled_ppm() for adjfine.
Patch v3 -> v4
- removed IRQF_TRIGGER_FALLING from the request_threaded_irq of ptp msg
- addressed review comments on patch 10 periodic output
- added sign off in patch 6 & 9
- reverted to set PTP_1STEP bit for lan937x which is missed during v3 regression
Patch v2-> v3
- used port_rxtstamp for reconstructing the absolute timestamp instead of
tagger function pointer.
- Reverted to setting of 802.1As bit.
Patch v1 -> v2
- GPIO perout enable bit is different for LAN937x and KSZ9x. Added new patch
for configuring LAN937x programmable pins.
- PTP enabled in hardware based on both tx and rx timestamping of all the user
ports.
- Replaced setting of 802.1AS bit with P2P bit in PTP_MSG_CONF1 register.
RFC v2 -> Patch v1
- Changed the patch author based on past patch submission
- Changed the commit message prefix as net: dsa: microchip: ptp
Individual patch changes are listed in correspondig commits.
RFC v1 -> v2
- Added the p2p1step timestamping and conditional execution of 2 step for
LAN937x only.
- Added the periodic output support
Arun Ramadoss (5):
net: dsa: microchip: ptp: add 4 bytes in tail tag when ptp enabled
net: dsa: microchip: ptp: enable interrupt for timestamping
net: dsa: microchip: ptp: add support for perout programmable pins
net: dsa: microchip: ptp: lan937x: add 2 step timestamping
net: dsa: microchip: ptp: lan937x: Enable periodic output in LED pins
Christian Eggers (8):
net: dsa: microchip: ptp: add the posix clock support
net: dsa: microchip: ptp: Initial hardware time stamping support
net: dsa: microchip: ptp: manipulating absolute time using ptp hw
clock
net: ptp: add helper for one-step P2P clocks
net: dsa: microchip: ptp: add packet reception timestamping
net: dsa: microchip: ptp: add packet transmission timestamping
net: dsa: microchip: ptp: move pdelay_rsp correction field to tail tag
net: dsa: microchip: ptp: add periodic output signal
MAINTAINERS | 1 +
drivers/net/dsa/microchip/Kconfig | 11 +
drivers/net/dsa/microchip/Makefile | 5 +
drivers/net/dsa/microchip/ksz_common.c | 44 +-
drivers/net/dsa/microchip/ksz_common.h | 48 +
drivers/net/dsa/microchip/ksz_ptp.c | 1199 +++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 86 ++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 142 +++
include/linux/dsa/ksz_common.h | 53 +
include/linux/ptp_classify.h | 71 ++
net/dsa/tag_ksz.c | 201 +++-
11 files changed, 1843 insertions(+), 18 deletions(-)
create mode 100644 drivers/net/dsa/microchip/ksz_ptp.c
create mode 100644 drivers/net/dsa/microchip/ksz_ptp.h
create mode 100644 drivers/net/dsa/microchip/ksz_ptp_reg.h
create mode 100644 include/linux/dsa/ksz_common.h
base-commit: c183e6c3ec342624c43269c099050d01eeb67e63
--
2.36.1
From: Christian Eggers <[email protected]>
This patch adds the routines for transmission of ptp packets. When the
ptp pdelay_req packet to be transmitted, it uses the deferred xmit
worker to schedule the packets.
During irq_setup, interrupt for Sync, Pdelay_req and Pdelay_rsp are
enabled. So interrupt is triggered for all three packets. But for
p2p1step, we require only time stamp of Pdelay_req packet. Hence to
avoid posting of the completion from ISR routine for Sync and
Pdelay_resp packets, ts_en flag is introduced. This controls which
packets need to processed for timestamp.
After the packet is transmitted, ISR is triggered. The time at which
packet transmitted is recorded to separate register.
This value is reconstructed to absolute time and posted to the user
application through socket error queue.
Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>
Reviewed-by: Vladimir Oltean <[email protected]>
---
v6 -> v7
- Replaced 0/1 with false/true for ts_en
- corrected the spaces in port_txtstamp()
- Set SKB_TX_INPROGRESS flag before dsa_enqueue_skb()
v1 -> v2
- Declared the deferred xmit and ksz_port_txtstamp function as null in
ptp disabled case
RFC v2 -> Patch v1
- separated the pdelay_rsp message correction update in different patch
---
drivers/net/dsa/microchip/ksz_common.c | 14 +++
drivers/net/dsa/microchip/ksz_common.h | 3 +
drivers/net/dsa/microchip/ksz_ptp.c | 115 ++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.h | 6 ++
include/linux/dsa/ksz_common.h | 8 ++
net/dsa/tag_ksz.c | 54 +++++++++++-
6 files changed, 196 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index b4e7d579ac51..5e1e5bd555d2 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -6,6 +6,7 @@
*/
#include <linux/delay.h>
+#include <linux/dsa/ksz_common.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
@@ -2539,6 +2540,17 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds,
return proto;
}
+static int ksz_connect_tag_protocol(struct dsa_switch *ds,
+ enum dsa_tag_protocol proto)
+{
+ struct ksz_tagger_data *tagger_data;
+
+ tagger_data = ksz_tagger_data(ds);
+ tagger_data->xmit_work_fn = ksz_port_deferred_xmit;
+
+ return 0;
+}
+
static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port,
bool flag, struct netlink_ext_ack *extack)
{
@@ -2954,6 +2966,7 @@ static int ksz_switch_detect(struct ksz_device *dev)
static const struct dsa_switch_ops ksz_switch_ops = {
.get_tag_protocol = ksz_get_tag_protocol,
+ .connect_tag_protocol = ksz_connect_tag_protocol,
.get_phy_flags = ksz_get_phy_flags,
.setup = ksz_setup,
.teardown = ksz_teardown,
@@ -2991,6 +3004,7 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.get_ts_info = ksz_get_ts_info,
.port_hwtstamp_get = ksz_hwtstamp_get,
.port_hwtstamp_set = ksz_hwtstamp_set,
+ .port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
};
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index ec1bceb4efcc..c8b49c86dfe1 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -87,6 +87,7 @@ struct ksz_irq {
struct ksz_ptp_irq {
struct ksz_port *port;
u16 ts_reg;
+ bool ts_en;
char name[16];
int num;
};
@@ -116,6 +117,8 @@ struct ksz_port {
bool hwts_rx_en;
struct ksz_irq ptpirq;
struct ksz_ptp_irq ptpmsg_irq[3];
+ ktime_t tstamp_msg;
+ struct completion tstamp_msg_comp;
#endif
};
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 29413fb608ed..6edce141cbd7 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -18,6 +18,8 @@
#define ptp_caps_to_data(d) container_of((d), struct ksz_ptp_data, caps)
#define ptp_data_to_ksz_dev(d) container_of((d), struct ksz_device, ptp_data)
+#define work_to_xmit_work(w) \
+ container_of((w), struct ksz_deferred_xmit_work, work)
/* Sub-nanoseconds-adj,max * sub-nanoseconds / 40ns * 1ns
* = (2^30-1) * (2 ^ 32) / 40 ns * 1 ns = 6249999
@@ -111,9 +113,15 @@ static int ksz_set_hwtstamp_config(struct ksz_device *dev,
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
+ prt->ptpmsg_irq[KSZ_SYNC_MSG].ts_en = false;
+ prt->ptpmsg_irq[KSZ_XDREQ_MSG].ts_en = false;
+ prt->ptpmsg_irq[KSZ_PDRES_MSG].ts_en = false;
prt->hwts_tx_en = false;
break;
case HWTSTAMP_TX_ONESTEP_P2P:
+ prt->ptpmsg_irq[KSZ_SYNC_MSG].ts_en = false;
+ prt->ptpmsg_irq[KSZ_XDREQ_MSG].ts_en = true;
+ prt->ptpmsg_irq[KSZ_PDRES_MSG].ts_en = false;
prt->hwts_tx_en = true;
break;
default:
@@ -232,6 +240,87 @@ bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
return false;
}
+void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ptp_header *hdr;
+ struct sk_buff *clone;
+ struct ksz_port *prt;
+ unsigned int type;
+ u8 ptp_msg_type;
+
+ prt = &dev->ports[port];
+
+ if (!prt->hwts_tx_en)
+ return;
+
+ type = ptp_classify_raw(skb);
+ if (type == PTP_CLASS_NONE)
+ return;
+
+ hdr = ptp_parse_header(skb, type);
+ if (!hdr)
+ return;
+
+ ptp_msg_type = ptp_get_msgtype(hdr, type);
+
+ switch (ptp_msg_type) {
+ case PTP_MSGTYPE_PDELAY_REQ:
+ break;
+
+ default:
+ return;
+ }
+
+ clone = skb_clone_sk(skb);
+ if (!clone)
+ return;
+
+ /* caching the value to be used in tag_ksz.c */
+ KSZ_SKB_CB(skb)->clone = clone;
+}
+
+static void ksz_ptp_txtstamp_skb(struct ksz_device *dev,
+ struct ksz_port *prt, struct sk_buff *skb)
+{
+ struct skb_shared_hwtstamps hwtstamps = {};
+ int ret;
+
+ /* timeout must include DSA master to transmit data, tstamp latency,
+ * IRQ latency and time for reading the time stamp.
+ */
+ ret = wait_for_completion_timeout(&prt->tstamp_msg_comp,
+ msecs_to_jiffies(100));
+ if (!ret)
+ return;
+
+ hwtstamps.hwtstamp = prt->tstamp_msg;
+ skb_complete_tx_timestamp(skb, &hwtstamps);
+}
+
+void ksz_port_deferred_xmit(struct kthread_work *work)
+{
+ struct ksz_deferred_xmit_work *xmit_work = work_to_xmit_work(work);
+ struct sk_buff *clone, *skb = xmit_work->skb;
+ struct dsa_switch *ds = xmit_work->dp->ds;
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *prt;
+
+ prt = &dev->ports[xmit_work->dp->index];
+
+ clone = KSZ_SKB_CB(skb)->clone;
+
+ skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
+
+ reinit_completion(&prt->tstamp_msg_comp);
+
+ dsa_enqueue_skb(skb, skb->dev);
+
+ ksz_ptp_txtstamp_skb(dev, prt, clone);
+
+ kfree(xmit_work);
+}
+
static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
{
u32 nanoseconds;
@@ -488,7 +577,29 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds)
static irqreturn_t ksz_ptp_msg_thread_fn(int irq, void *dev_id)
{
- return IRQ_NONE;
+ struct ksz_ptp_irq *ptpmsg_irq = dev_id;
+ struct ksz_device *dev;
+ struct ksz_port *port;
+ u32 tstamp_raw;
+ ktime_t tstamp;
+ int ret;
+
+ port = ptpmsg_irq->port;
+ dev = port->ksz_dev;
+
+ if (ptpmsg_irq->ts_en) {
+ ret = ksz_read32(dev, ptpmsg_irq->ts_reg, &tstamp_raw);
+ if (ret)
+ return IRQ_NONE;
+
+ tstamp = ksz_decode_tstamp(tstamp_raw);
+
+ port->tstamp_msg = ksz_tstamp_reconstruct(dev, tstamp);
+
+ complete(&port->tstamp_msg_comp);
+ }
+
+ return IRQ_HANDLED;
}
static irqreturn_t ksz_ptp_irq_thread_fn(int irq, void *dev_id)
@@ -633,6 +744,8 @@ int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
REG_PTP_PORT_TX_INT_STATUS__2);
snprintf(ptpirq->name, sizeof(ptpirq->name), "ptp-irq-%d", p);
+ init_completion(&port->tstamp_msg_comp);
+
ptpirq->domain = irq_domain_add_linear(dev->dev->of_node, ptpirq->nirqs,
&ksz_ptp_irq_domain_ops, ptpirq);
if (!ptpirq->domain)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 9bb8fb059ac2..0b14aed71ec2 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -30,6 +30,8 @@ int ksz_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *ts);
int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);
+void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb);
+void ksz_port_deferred_xmit(struct kthread_work *work);
bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
unsigned int type);
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p);
@@ -64,6 +66,10 @@ static inline void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) {}
#define ksz_port_rxtstamp NULL
+#define ksz_port_txtstamp NULL
+
+#define ksz_port_deferred_xmit NULL
+
#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */
#endif
diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h
index a256b08d837d..b91beab5e138 100644
--- a/include/linux/dsa/ksz_common.h
+++ b/include/linux/dsa/ksz_common.h
@@ -23,11 +23,19 @@ static inline ktime_t ksz_decode_tstamp(u32 tstamp)
return ns_to_ktime(ns);
}
+struct ksz_deferred_xmit_work {
+ struct dsa_port *dp;
+ struct sk_buff *skb;
+ struct kthread_work work;
+};
+
struct ksz_tagger_data {
+ void (*xmit_work_fn)(struct kthread_work *work);
void (*hwtstamp_set_state)(struct dsa_switch *ds, bool on);
};
struct ksz_skb_cb {
+ struct sk_buff *clone;
u32 tstamp;
};
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 6603eaa234d2..e14ee26bf6a0 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -26,6 +26,7 @@
struct ksz_tagger_private {
struct ksz_tagger_data data; /* Must be first */
unsigned long state;
+ struct kthread_worker *xmit_worker;
};
static struct ksz_tagger_private *
@@ -48,6 +49,7 @@ static void ksz_disconnect(struct dsa_switch *ds)
{
struct ksz_tagger_private *priv = ds->tagger_data;
+ kthread_destroy_worker(priv->xmit_worker);
kfree(priv);
ds->tagger_data = NULL;
}
@@ -55,12 +57,23 @@ static void ksz_disconnect(struct dsa_switch *ds)
static int ksz_connect(struct dsa_switch *ds)
{
struct ksz_tagger_data *tagger_data;
+ struct kthread_worker *xmit_worker;
struct ksz_tagger_private *priv;
+ int ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ xmit_worker = kthread_create_worker(0, "dsa%d:%d_xmit",
+ ds->dst->index, ds->index);
+ if (IS_ERR(xmit_worker)) {
+ ret = PTR_ERR(xmit_worker);
+ kfree(priv);
+ return ret;
+ }
+
+ priv->xmit_worker = xmit_worker;
/* Export functions for switch driver use */
tagger_data = &priv->data;
tagger_data->hwtstamp_set_state = ksz_hwtstamp_set_state;
@@ -191,6 +204,41 @@ static void ksz_xmit_timestamp(struct dsa_port *dp, struct sk_buff *skb)
put_unaligned_be32(0, skb_put(skb, KSZ_PTP_TAG_LEN));
}
+/* Defer transmit if waiting for egress time stamp is required. */
+static struct sk_buff *ksz_defer_xmit(struct dsa_port *dp, struct sk_buff *skb)
+{
+ struct ksz_tagger_data *tagger_data = ksz_tagger_data(dp->ds);
+ struct ksz_tagger_private *priv = ksz_tagger_private(dp->ds);
+ void (*xmit_work_fn)(struct kthread_work *work);
+ struct sk_buff *clone = KSZ_SKB_CB(skb)->clone;
+ struct ksz_deferred_xmit_work *xmit_work;
+ struct kthread_worker *xmit_worker;
+
+ if (!clone)
+ return skb; /* no deferred xmit for this packet */
+
+ xmit_work_fn = tagger_data->xmit_work_fn;
+ xmit_worker = priv->xmit_worker;
+
+ if (!xmit_work_fn || !xmit_worker)
+ return NULL;
+
+ xmit_work = kzalloc(sizeof(*xmit_work), GFP_ATOMIC);
+ if (!xmit_work)
+ return NULL;
+
+ kthread_init_work(&xmit_work->work, xmit_work_fn);
+ /* Increase refcount so the kfree_skb in dsa_slave_xmit
+ * won't really free the packet.
+ */
+ xmit_work->dp = dp;
+ xmit_work->skb = skb_get(skb);
+
+ kthread_queue_work(xmit_worker, &xmit_work->work);
+
+ return NULL;
+}
+
static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -215,7 +263,7 @@ static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
*tag = cpu_to_be16(val);
- return skb;
+ return ksz_defer_xmit(dp, skb);
}
static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev)
@@ -271,7 +319,7 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,
if (is_link_local_ether_addr(addr))
*tag |= KSZ9893_TAIL_TAG_OVERRIDE;
- return skb;
+ return ksz_defer_xmit(dp, skb);
}
static const struct dsa_device_ops ksz9893_netdev_ops = {
@@ -336,7 +384,7 @@ static struct sk_buff *lan937x_xmit(struct sk_buff *skb,
put_unaligned_be16(val, tag);
- return skb;
+ return ksz_defer_xmit(dp, skb);
}
static const struct dsa_device_ops lan937x_netdev_ops = {
--
2.36.1
From: Christian Eggers <[email protected]>
This patch adds the routine for get_ts_info, hwstamp_get, set. This enables
the PTP support towards userspace applications such as linuxptp.
Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>
---
v6 -> v7
- corrected the spaces and tabs misplacement
v1 -> v2
- Declared the ksz_hwtstamp_get/set to NULL as macro if ptp is not
enabled
- Removed mutex lock in hwtstamp_set()
RFC v2 -> Patch v1
- moved tagger set and get function to separate patch
- Removed unnecessary comments
---
drivers/net/dsa/microchip/ksz_common.c | 3 +
drivers/net/dsa/microchip/ksz_common.h | 3 +
drivers/net/dsa/microchip/ksz_ptp.c | 101 +++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 11 +++
4 files changed, 118 insertions(+)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 3e2ebadeade9..1dddb80a2baf 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2977,6 +2977,9 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.get_pause_stats = ksz_get_pause_stats,
.port_change_mtu = ksz_change_mtu,
.port_max_mtu = ksz_max_mtu,
+ .get_ts_info = ksz_get_ts_info,
+ .port_hwtstamp_get = ksz_hwtstamp_get,
+ .port_hwtstamp_set = ksz_hwtstamp_set,
};
struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 23ed7fa72a3c..a5ce7ec30ba2 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -102,6 +102,9 @@ struct ksz_port {
struct ksz_device *ksz_dev;
struct ksz_irq pirq;
u8 num;
+#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
+ struct hwtstamp_config tstamp_config;
+#endif
};
struct ksz_device {
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index fb1efb60ef71..6f6747671610 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -24,6 +24,107 @@
#define KSZ_PTP_INC_NS 40ULL /* HW clock is incremented every 40 ns (by 40) */
#define KSZ_PTP_SUBNS_BITS 32
+/* The function is return back the capability of timestamping feature when
+ * requested through ethtool -T <interface> utility
+ */
+int ksz_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+
+ ptp_data = &dev->ptp_data;
+
+ if (!ptp_data->clock)
+ return -ENODEV;
+
+ ts->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ONESTEP_P2P);
+
+ ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
+
+ ts->phc_index = ptp_clock_index(ptp_data->clock);
+
+ return 0;
+}
+
+int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)
+{
+ struct ksz_device *dev = ds->priv;
+ struct hwtstamp_config *config;
+ struct ksz_port *prt;
+
+ prt = &dev->ports[port];
+ config = &prt->tstamp_config;
+
+ return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+ -EFAULT : 0;
+}
+
+static int ksz_set_hwtstamp_config(struct ksz_device *dev,
+ struct hwtstamp_config *config)
+{
+ if (config->flags)
+ return -EINVAL;
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ case HWTSTAMP_TX_ONESTEP_P2P:
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ break;
+ default:
+ config->rx_filter = HWTSTAMP_FILTER_NONE;
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)
+{
+ struct ksz_device *dev = ds->priv;
+ struct hwtstamp_config config;
+ struct ksz_port *prt;
+ int ret;
+
+ prt = &dev->ports[port];
+
+ ret = copy_from_user(&config, ifr->ifr_data, sizeof(config));
+ if (ret)
+ return ret;
+
+ ret = ksz_set_hwtstamp_config(dev, &config);
+ if (ret)
+ return ret;
+
+ memcpy(&prt->tstamp_config, &config, sizeof(config));
+
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config));
+}
+
static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
{
u32 nanoseconds;
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 8930047da764..7bb3fde2dd14 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -23,6 +23,11 @@ int ksz_ptp_clock_register(struct dsa_switch *ds);
void ksz_ptp_clock_unregister(struct dsa_switch *ds);
+int ksz_get_ts_info(struct dsa_switch *ds, int port,
+ struct ethtool_ts_info *ts);
+int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
+int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);
+
#else
struct ksz_ptp_data {
@@ -37,6 +42,12 @@ static inline int ksz_ptp_clock_register(struct dsa_switch *ds)
static inline void ksz_ptp_clock_unregister(struct dsa_switch *ds) { }
+#define ksz_get_ts_info NULL
+
+#define ksz_hwtstamp_get NULL
+
+#define ksz_hwtstamp_set NULL
+
#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */
#endif
--
2.36.1
From: Christian Eggers <[email protected]>
Rx Timestamping is done through 4 additional bytes in tail tag.
Whenever the ptp packet is received, the 4 byte hardware time stamped
value is added before 1 byte tail tag. Also, bit 7 in tail tag indicates
it as PTP frame. This 4 byte value is extracted from the tail tag and
reconstructed to absolute time and assigned to skb hwtstamp.
If the packet received in PDelay_Resp, then partial ingress timestamp
is subtracted from the correction field. Since user space tools expects
to be done in hardware.
Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>
Reviewed-by: Vladimir Oltean <[email protected]>
---
v3 - v4
- replaced 0 with false in return value of port_rxtstamp
v2 - v3
- Replaced tagger_data->meta_timestamper handler with port_rxtstamp
routine
v1 - v2
- Checkpatch warning line limit to 80chars
RFC v2 -> Patch v1
- Fixed compilation issue
---
drivers/net/dsa/microchip/ksz_common.c | 1 +
drivers/net/dsa/microchip/ksz_ptp.c | 63 ++++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 4 ++
include/linux/dsa/ksz_common.h | 21 +++++++++
net/dsa/tag_ksz.c | 25 +++++++---
5 files changed, 108 insertions(+), 6 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index bdd068322ca0..b4e7d579ac51 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2991,6 +2991,7 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.get_ts_info = ksz_get_ts_info,
.port_hwtstamp_get = ksz_hwtstamp_get,
.port_hwtstamp_set = ksz_hwtstamp_set,
+ .port_rxtstamp = ksz_port_rxtstamp,
};
struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 6cf30bf50c7e..29413fb608ed 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -169,6 +169,69 @@ int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)
return copy_to_user(ifr->ifr_data, &config, sizeof(config));
}
+static ktime_t ksz_tstamp_reconstruct(struct ksz_device *dev, ktime_t tstamp)
+{
+ struct timespec64 ptp_clock_time;
+ struct ksz_ptp_data *ptp_data;
+ struct timespec64 diff;
+ struct timespec64 ts;
+
+ ptp_data = &dev->ptp_data;
+ ts = ktime_to_timespec64(tstamp);
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_clock_time = ptp_data->clock_time;
+ spin_unlock_bh(&ptp_data->clock_lock);
+
+ /* calculate full time from partial time stamp */
+ ts.tv_sec = (ptp_clock_time.tv_sec & ~3) | ts.tv_sec;
+
+ /* find nearest possible point in time */
+ diff = timespec64_sub(ts, ptp_clock_time);
+ if (diff.tv_sec > 2)
+ ts.tv_sec -= 4;
+ else if (diff.tv_sec < -2)
+ ts.tv_sec += 4;
+
+ return timespec64_to_ktime(ts);
+}
+
+bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
+ unsigned int type)
+{
+ struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
+ struct ksz_device *dev = ds->priv;
+ struct ptp_header *ptp_hdr;
+ u8 ptp_msg_type;
+ ktime_t tstamp;
+ s64 correction;
+
+ tstamp = KSZ_SKB_CB(skb)->tstamp;
+ memset(hwtstamps, 0, sizeof(*hwtstamps));
+ hwtstamps->hwtstamp = ksz_tstamp_reconstruct(dev, tstamp);
+
+ ptp_hdr = ptp_parse_header(skb, type);
+ if (!ptp_hdr)
+ goto out;
+
+ ptp_msg_type = ptp_get_msgtype(ptp_hdr, type);
+ if (ptp_msg_type != PTP_MSGTYPE_PDELAY_REQ)
+ goto out;
+
+ /* Only subtract the partial time stamp from the correction field. When
+ * the hardware adds the egress time stamp to the correction field of
+ * the PDelay_Resp message on tx, also only the partial time stamp will
+ * be added.
+ */
+ correction = (s64)get_unaligned_be64(&ptp_hdr->correction);
+ correction -= ktime_to_ns(tstamp) << 16;
+
+ ptp_header_update_correction(skb, type, ptp_hdr, correction);
+
+out:
+ return false;
+}
+
static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
{
u32 nanoseconds;
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 7c5679372705..9bb8fb059ac2 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -30,6 +30,8 @@ int ksz_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *ts);
int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);
+bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
+ unsigned int type);
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p);
void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p);
@@ -60,6 +62,8 @@ static inline void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) {}
#define ksz_hwtstamp_set NULL
+#define ksz_port_rxtstamp NULL
+
#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */
#endif
diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h
index d2a54161be97..a256b08d837d 100644
--- a/include/linux/dsa/ksz_common.h
+++ b/include/linux/dsa/ksz_common.h
@@ -9,10 +9,31 @@
#include <net/dsa.h>
+/* All time stamps from the KSZ consist of 2 bits for seconds and 30 bits for
+ * nanoseconds. This is NOT the same as 32 bits for nanoseconds.
+ */
+#define KSZ_TSTAMP_SEC_MASK GENMASK(31, 30)
+#define KSZ_TSTAMP_NSEC_MASK GENMASK(29, 0)
+
+static inline ktime_t ksz_decode_tstamp(u32 tstamp)
+{
+ u64 ns = FIELD_GET(KSZ_TSTAMP_SEC_MASK, tstamp) * NSEC_PER_SEC +
+ FIELD_GET(KSZ_TSTAMP_NSEC_MASK, tstamp);
+
+ return ns_to_ktime(ns);
+}
+
struct ksz_tagger_data {
void (*hwtstamp_set_state)(struct dsa_switch *ds, bool on);
};
+struct ksz_skb_cb {
+ u32 tstamp;
+};
+
+#define KSZ_SKB_CB(skb) \
+ ((struct ksz_skb_cb *)((skb)->cb))
+
static inline struct ksz_tagger_data *
ksz_tagger_data(struct dsa_switch *ds)
{
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 420a12853676..6603eaa234d2 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -151,10 +151,11 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
* tag0 : Prioritization (not used now)
* tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
*
- * For Egress (KSZ9477 -> Host), 1 byte is added before FCS.
+ * For Egress (KSZ9477 -> Host), 1/5 bytes is added before FCS.
* ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|FCS(4bytes)
* ---------------------------------------------------------------------------
+ * ts : time stamp (Present only if bit 7 of tag0 is set)
* tag0 : zero-based value represents port
* (eg, 0x00=port1, 0x02=port3, 0x06=port7)
*/
@@ -166,6 +167,15 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
#define KSZ9477_TAIL_TAG_OVERRIDE BIT(9)
#define KSZ9477_TAIL_TAG_LOOKUP BIT(10)
+static void ksz_rcv_timestamp(struct sk_buff *skb, u8 *tag)
+{
+ u8 *tstamp_raw = tag - KSZ_PTP_TAG_LEN;
+ ktime_t tstamp;
+
+ tstamp = ksz_decode_tstamp(get_unaligned_be32(tstamp_raw));
+ KSZ_SKB_CB(skb)->tstamp = tstamp;
+}
+
/* Time stamp tag *needs* to be inserted if PTP is enabled in hardware.
* Regardless of Whether it is a PTP frame or not.
*/
@@ -216,8 +226,10 @@ static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev)
unsigned int len = KSZ_EGRESS_TAG_LEN;
/* Extra 4-bytes PTP timestamp */
- if (tag[0] & KSZ9477_PTP_TAG_INDICATION)
- len += KSZ9477_PTP_TAG_LEN;
+ if (tag[0] & KSZ9477_PTP_TAG_INDICATION) {
+ ksz_rcv_timestamp(skb, tag);
+ len += KSZ_PTP_TAG_LEN;
+ }
return ksz_common_rcv(skb, dev, port, len);
}
@@ -284,10 +296,11 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893, KSZ9893_NAME);
* tag0 : represents tag override, lookup and valid
* tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x80=port8)
*
- * For rcv, 1 byte is added before FCS.
+ * For rcv, 1/5 bytes is added before FCS.
* ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|FCS(4bytes)
* ---------------------------------------------------------------------------
+ * ts : time stamp (Present only if bit 7 of tag0 is set)
* tag0 : zero-based value represents port
* (eg, 0x00=port1, 0x02=port3, 0x07=port8)
*/
--
2.36.1
From: Christian Eggers <[email protected]>
This patch is used for reconstructing the absolute time from the 32bit
hardware time stamping value. The do_aux ioctl is used for reading the
ptp hardware clock and store it to global variable.
The timestamped value in tail tag during rx and register during tx are
32 bit value (2 bit seconds and 30 bit nanoseconds). The time taken to
read entire ptp clock will be time consuming. In order to speed up, the
software clock is maintained. This clock time will be added to 32 bit
timestamp to get the absolute time stamp.
Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>
---
v6 -> v7
- Added the mutex lock in the do_aux_work() to avoid race condition
v1 -> v2
- Used ksz_ptp_gettime instead of _ksz_ptp_gettime in do_aux_work()
- Removed the spin_lock_bh in the ksz_ptp_start_clock()
RFC v1
- This patch is based on Christian Eggers Initial hardware timestamping
support
---
drivers/net/dsa/microchip/ksz_ptp.c | 60 ++++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.h | 3 ++
2 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 5281aeb84db6..3e124816697d 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -28,9 +28,11 @@
static int ksz_ptp_enable_mode(struct ksz_device *dev)
{
struct ksz_tagger_data *tagger_data = ksz_tagger_data(dev->ds);
+ struct ksz_ptp_data *ptp_data = &dev->ptp_data;
struct ksz_port *prt;
struct dsa_port *dp;
bool tag_en = false;
+ int ret;
dsa_switch_for_each_user_port(dp, dev->ds) {
prt = &dev->ports[dp->index];
@@ -40,6 +42,14 @@ static int ksz_ptp_enable_mode(struct ksz_device *dev)
}
}
+ if (tag_en) {
+ ret = ptp_schedule_worker(ptp_data->clock, 0);
+ if (ret)
+ return ret;
+ } else {
+ ptp_cancel_worker_sync(ptp_data->clock);
+ }
+
tagger_data->hwtstamp_set_state(dev->ds, tag_en);
return ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_ENABLE,
@@ -221,6 +231,12 @@ static int ksz_ptp_settime(struct ptp_clock_info *ptp,
goto unlock;
ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME, PTP_LOAD_TIME);
+ if (ret)
+ goto unlock;
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time = *ts;
+ spin_unlock_bh(&ptp_data->clock_lock);
unlock:
mutex_unlock(&ptp_data->lock);
@@ -271,6 +287,7 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ struct timespec64 delta64 = ns_to_timespec64(delta);
s32 sec, nsec;
u16 data16;
int ret;
@@ -303,15 +320,54 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
data16 |= PTP_STEP_DIR;
ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16);
+ if (ret)
+ goto unlock;
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time = timespec64_add(ptp_data->clock_time, delta64);
+ spin_unlock_bh(&ptp_data->clock_lock);
unlock:
mutex_unlock(&ptp_data->lock);
return ret;
}
+/* Function is pointer to the do_aux_work in the ptp_clock capability */
+static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ struct timespec64 ts;
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+ ret = _ksz_ptp_gettime(dev, &ts);
+ if (ret)
+ goto out;
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time = ts;
+ spin_unlock_bh(&ptp_data->clock_lock);
+
+out:
+ mutex_unlock(&ptp_data->lock);
+
+ return HZ; /* reschedule in 1 second */
+}
+
static int ksz_ptp_start_clock(struct ksz_device *dev)
{
- return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
+ struct ksz_ptp_data *ptp_data = &dev->ptp_data;
+ int ret;
+
+ ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
+ if (ret)
+ return ret;
+
+ ptp_data->clock_time.tv_sec = 0;
+ ptp_data->clock_time.tv_nsec = 0;
+
+ return 0;
}
int ksz_ptp_clock_register(struct dsa_switch *ds)
@@ -322,6 +378,7 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
ptp_data = &dev->ptp_data;
mutex_init(&ptp_data->lock);
+ spin_lock_init(&ptp_data->clock_lock);
ptp_data->caps.owner = THIS_MODULE;
snprintf(ptp_data->caps.name, 16, "Microchip Clock");
@@ -330,6 +387,7 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
ptp_data->caps.settime64 = ksz_ptp_settime;
ptp_data->caps.adjfine = ksz_ptp_adjfine;
ptp_data->caps.adjtime = ksz_ptp_adjtime;
+ ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work;
ret = ksz_ptp_start_clock(dev);
if (ret)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 7bb3fde2dd14..2c29a0b604bb 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -17,6 +17,9 @@ struct ksz_ptp_data {
struct ptp_clock *clock;
/* Serializes all operations on the PTP hardware clock */
struct mutex lock;
+ /* lock for accessing the clock_time */
+ spinlock_t clock_lock;
+ struct timespec64 clock_time;
};
int ksz_ptp_clock_register(struct dsa_switch *ds);
--
2.36.1
LAN937x series of switches support 2 step timestamping mechanism. There
are timestamp correction calculation performed in ksz_rcv_timestamp and
ksz_xmit_timestamp which are applicable only for p2p1step. To check
whether the 2 step is enabled or not in tag_ksz.c introduced the helper
function in taggger_data to query it from ksz_ptp.c. Based on whether 2
step is enabled or not, timestamp calculation are performed.
Signed-off-by: Arun Ramadoss <[email protected]>
---
v6 -> v7
- s/1/true in hwtstamp_config()
v3 -> v4
- P2P_1step bit is set which is required for P2P. It is missed during
patch v3 regression.
v2 -> v3
- Reverted setting PTP_1Step bit as we are setting 802_1AS bit
v1 -> v2
- declard is_ptp_twostep as macro NULL for ptp disabled case
- Moved the patch in series to have continuity for lan937x updates 9/11
to 12/13
- enable PTP_1STEP bit based on tx timestamping
Patch v1
- Patch is new.
---
drivers/net/dsa/microchip/ksz_ptp.c | 43 +++++++++++++++++++++++++++--
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 03fbbe6493ed..3ba36d33e830 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -283,6 +283,9 @@ int ksz_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts)
ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ONESTEP_P2P);
+ if (is_lan937x(dev))
+ ts->tx_types |= BIT(HWTSTAMP_TX_ON);
+
ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
@@ -310,6 +313,8 @@ static int ksz_set_hwtstamp_config(struct ksz_device *dev,
struct ksz_port *prt,
struct hwtstamp_config *config)
{
+ int ret;
+
if (config->flags)
return -EINVAL;
@@ -325,6 +330,25 @@ static int ksz_set_hwtstamp_config(struct ksz_device *dev,
prt->ptpmsg_irq[KSZ_XDREQ_MSG].ts_en = true;
prt->ptpmsg_irq[KSZ_PDRES_MSG].ts_en = false;
prt->hwts_tx_en = true;
+
+ ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_1STEP, PTP_1STEP);
+ if (ret)
+ return ret;
+
+ break;
+ case HWTSTAMP_TX_ON:
+ if (!is_lan937x(dev))
+ return -ERANGE;
+
+ prt->ptpmsg_irq[KSZ_SYNC_MSG].ts_en = true;
+ prt->ptpmsg_irq[KSZ_XDREQ_MSG].ts_en = true;
+ prt->ptpmsg_irq[KSZ_PDRES_MSG].ts_en = true;
+ prt->hwts_tx_en = true;
+
+ ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_1STEP, 0);
+ if (ret)
+ return ret;
+
break;
default:
return -ERANGE;
@@ -412,14 +436,20 @@ bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
struct ksz_device *dev = ds->priv;
struct ptp_header *ptp_hdr;
+ struct ksz_port *prt;
u8 ptp_msg_type;
ktime_t tstamp;
s64 correction;
+ prt = &dev->ports[port];
+
tstamp = KSZ_SKB_CB(skb)->tstamp;
memset(hwtstamps, 0, sizeof(*hwtstamps));
hwtstamps->hwtstamp = ksz_tstamp_reconstruct(dev, tstamp);
+ if (prt->tstamp_config.tx_type != HWTSTAMP_TX_ONESTEP_P2P)
+ goto out;
+
ptp_hdr = ptp_parse_header(skb, type);
if (!ptp_hdr)
goto out;
@@ -467,12 +497,19 @@ void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
ptp_msg_type = ptp_get_msgtype(hdr, type);
switch (ptp_msg_type) {
+ case PTP_MSGTYPE_SYNC:
+ if (prt->tstamp_config.tx_type == HWTSTAMP_TX_ONESTEP_P2P)
+ return;
+ break;
case PTP_MSGTYPE_PDELAY_REQ:
break;
case PTP_MSGTYPE_PDELAY_RESP:
- KSZ_SKB_CB(skb)->ptp_type = type;
- KSZ_SKB_CB(skb)->update_correction = true;
- return;
+ if (prt->tstamp_config.tx_type == HWTSTAMP_TX_ONESTEP_P2P) {
+ KSZ_SKB_CB(skb)->ptp_type = type;
+ KSZ_SKB_CB(skb)->update_correction = true;
+ return;
+ }
+ break;
default:
return;
--
2.36.1
From: Christian Eggers <[email protected]>
For PDelay_Resp messages we will likely have a negative value in the
correction field. The switch hardware cannot correctly update such
values (produces an off by one error in the UDP checksum), so it must be
moved to the time stamp field in the tail tag. Format of the correction
field is 48 bit ns + 16 bit fractional ns. After updating the
correction field, clone is no longer required hence it is freed.
Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>
---
v6 -> v7
- Reverted the fallthrough keyword
- updated pdelay_resp correction without skb clone
- removed local variable update_correction in tag_ksz.c
v2 -> v3
- used update_correction variable in skb->cb instead of ptp_msg_type
v1 -> v2
- added fallthrough keyword in switch case to suppress checkpatch
warning
RFC v3 -> Patch v1
- Patch is separated from transmission logic patch
---
drivers/net/dsa/microchip/ksz_ptp.c | 4 ++++
include/linux/dsa/ksz_common.h | 2 ++
net/dsa/tag_ksz.c | 29 ++++++++++++++++++++++++++++-
3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 6edce141cbd7..2a68649943d5 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -267,6 +267,10 @@ void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
switch (ptp_msg_type) {
case PTP_MSGTYPE_PDELAY_REQ:
break;
+ case PTP_MSGTYPE_PDELAY_RESP:
+ KSZ_SKB_CB(skb)->ptp_type = type;
+ KSZ_SKB_CB(skb)->update_correction = true;
+ return;
default:
return;
diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h
index b91beab5e138..576a99ca698d 100644
--- a/include/linux/dsa/ksz_common.h
+++ b/include/linux/dsa/ksz_common.h
@@ -36,6 +36,8 @@ struct ksz_tagger_data {
struct ksz_skb_cb {
struct sk_buff *clone;
+ unsigned int ptp_type;
+ bool update_correction;
u32 tstamp;
};
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index e14ee26bf6a0..694478fe07d6 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -7,6 +7,7 @@
#include <linux/dsa/ksz_common.h>
#include <linux/etherdevice.h>
#include <linux/list.h>
+#include <linux/ptp_classify.h>
#include <net/dsa.h>
#include "tag.h"
@@ -195,13 +196,39 @@ static void ksz_rcv_timestamp(struct sk_buff *skb, u8 *tag)
static void ksz_xmit_timestamp(struct dsa_port *dp, struct sk_buff *skb)
{
struct ksz_tagger_private *priv;
+ struct ptp_header *ptp_hdr;
+ unsigned int ptp_type;
+ u32 tstamp_raw = 0;
+ s64 correction;
priv = ksz_tagger_private(dp->ds);
if (!test_bit(KSZ_HWTS_EN, &priv->state))
return;
- put_unaligned_be32(0, skb_put(skb, KSZ_PTP_TAG_LEN));
+ if (!KSZ_SKB_CB(skb)->update_correction)
+ goto output_tag;
+
+ ptp_type = KSZ_SKB_CB(skb)->ptp_type;
+
+ ptp_hdr = ptp_parse_header(skb, ptp_type);
+ if (!ptp_hdr)
+ goto output_tag;
+
+ correction = (s64)get_unaligned_be64(&ptp_hdr->correction);
+
+ if (correction < 0) {
+ struct timespec64 ts;
+
+ ts = ns_to_timespec64(-correction >> 16);
+ tstamp_raw = ((ts.tv_sec & 3) << 30) | ts.tv_nsec;
+
+ /* Set correction field to 0 and update UDP checksum */
+ ptp_header_update_correction(skb, ptp_type, ptp_hdr, 0);
+ }
+
+output_tag:
+ put_unaligned_be32(tstamp_raw, skb_put(skb, KSZ_PTP_TAG_LEN));
}
/* Defer transmit if waiting for egress time stamp is required. */
--
2.36.1
From: Christian Eggers <[email protected]>
For P2P delay measurement, the ingress time stamp of the PDelay_Req is
required for the correction field of the PDelay_Resp. The application
echoes back the correction field of the PDelay_Req when sending the
PDelay_Resp.
Some hardware (like the ZHAW InES PTP time stamping IP core) subtracts
the ingress timestamp autonomously from the correction field, so that
the hardware only needs to add the egress timestamp on tx. Other
hardware (like the Microchip KSZ9563) reports the ingress time stamp via
an interrupt and requires that the software provides this time stamp via
tail-tag on tx.
In order to avoid introducing a further application interface for this,
the driver can simply emulate the behavior of the InES device and
subtract the ingress time stamp in software from the correction field.
On egress, the correction field can either be kept as it is (and the
time stamp field in the tail-tag is set to zero) or move the value from
the correction field back to the tail-tag.
Changing the correction field requires updating the UDP checksum (if UDP
is used as transport).
Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>
---
v1 -> v2
- Fixed compilation issue when PTP_CLASSIFY not selected in menuconfig
as reported by kernel test robot <[email protected]>
---
include/linux/ptp_classify.h | 71 ++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h
index 2b6ea36ad162..6e5869c2504c 100644
--- a/include/linux/ptp_classify.h
+++ b/include/linux/ptp_classify.h
@@ -10,8 +10,12 @@
#ifndef _PTP_CLASSIFY_H_
#define _PTP_CLASSIFY_H_
+#include <asm/unaligned.h>
#include <linux/ip.h>
+#include <linux/ktime.h>
#include <linux/skbuff.h>
+#include <linux/udp.h>
+#include <net/checksum.h>
#define PTP_CLASS_NONE 0x00 /* not a PTP event message */
#define PTP_CLASS_V1 0x01 /* protocol version 1 */
@@ -129,6 +133,67 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr,
return msgtype;
}
+/**
+ * ptp_check_diff8 - Computes new checksum (when altering a 64-bit field)
+ * @old: old field value
+ * @new: new field value
+ * @oldsum: previous checksum
+ *
+ * This function can be used to calculate a new checksum when only a single
+ * field is changed. Similar as ip_vs_check_diff*() in ip_vs.h.
+ *
+ * Return: Updated checksum
+ */
+static inline __wsum ptp_check_diff8(__be64 old, __be64 new, __wsum oldsum)
+{
+ __be64 diff[2] = { ~old, new };
+
+ return csum_partial(diff, sizeof(diff), oldsum);
+}
+
+/**
+ * ptp_header_update_correction - Update PTP header's correction field
+ * @skb: packet buffer
+ * @type: type of the packet (see ptp_classify_raw())
+ * @hdr: ptp header
+ * @correction: new correction value
+ *
+ * This updates the correction field of a PTP header and updates the UDP
+ * checksum (if UDP is used as transport). It is needed for hardware capable of
+ * one-step P2P that does not already modify the correction field of Pdelay_Req
+ * event messages on ingress.
+ */
+static inline
+void ptp_header_update_correction(struct sk_buff *skb, unsigned int type,
+ struct ptp_header *hdr, s64 correction)
+{
+ __be64 correction_old;
+ struct udphdr *uhdr;
+
+ /* previous correction value is required for checksum update. */
+ memcpy(&correction_old, &hdr->correction, sizeof(correction_old));
+
+ /* write new correction value */
+ put_unaligned_be64((u64)correction, &hdr->correction);
+
+ switch (type & PTP_CLASS_PMASK) {
+ case PTP_CLASS_IPV4:
+ case PTP_CLASS_IPV6:
+ /* locate udp header */
+ uhdr = (struct udphdr *)((char *)hdr - sizeof(struct udphdr));
+ break;
+ default:
+ return;
+ }
+
+ /* update checksum */
+ uhdr->check = csum_fold(ptp_check_diff8(correction_old,
+ hdr->correction,
+ ~csum_unfold(uhdr->check)));
+ if (!uhdr->check)
+ uhdr->check = CSUM_MANGLED_0;
+}
+
/**
* ptp_msg_is_sync - Evaluates whether the given skb is a PTP Sync message
* @skb: packet buffer
@@ -166,5 +231,11 @@ static inline bool ptp_msg_is_sync(struct sk_buff *skb, unsigned int type)
{
return false;
}
+
+static inline
+void ptp_header_update_correction(struct sk_buff *skb, unsigned int type,
+ struct ptp_header *hdr, s64 correction)
+{
+}
#endif
#endif /* _PTP_CLASSIFY_H_ */
--
2.36.1
On Wed, Jan 04, 2023 at 02:13:07PM +0530, Arun Ramadoss wrote:
> From: Christian Eggers <[email protected]>
>
> This patch is used for reconstructing the absolute time from the 32bit
> hardware time stamping value. The do_aux ioctl is used for reading the
> ptp hardware clock and store it to global variable.
> The timestamped value in tail tag during rx and register during tx are
> 32 bit value (2 bit seconds and 30 bit nanoseconds). The time taken to
> read entire ptp clock will be time consuming. In order to speed up, the
> software clock is maintained. This clock time will be added to 32 bit
> timestamp to get the absolute time stamp.
>
> Signed-off-by: Christian Eggers <[email protected]>
> Co-developed-by: Arun Ramadoss <[email protected]>
> Signed-off-by: Arun Ramadoss <[email protected]>
> ---
Reviewed-by: Vladimir Oltean <[email protected]>
Hi,
On Wed, 2023-01-04 at 14:13 +0530, Arun Ramadoss wrote:
> From: Christian Eggers <[email protected]>
>
> For P2P delay measurement, the ingress time stamp of the PDelay_Req is
> required for the correction field of the PDelay_Resp. The application
> echoes back the correction field of the PDelay_Req when sending the
> PDelay_Resp.
>
> Some hardware (like the ZHAW InES PTP time stamping IP core) subtracts
> the ingress timestamp autonomously from the correction field, so that
> the hardware only needs to add the egress timestamp on tx. Other
> hardware (like the Microchip KSZ9563) reports the ingress time stamp via
> an interrupt and requires that the software provides this time stamp via
> tail-tag on tx.
>
> In order to avoid introducing a further application interface for this,
> the driver can simply emulate the behavior of the InES device and
> subtract the ingress time stamp in software from the correction field.
>
> On egress, the correction field can either be kept as it is (and the
> time stamp field in the tail-tag is set to zero) or move the value from
> the correction field back to the tail-tag.
>
> Changing the correction field requires updating the UDP checksum (if UDP
> is used as transport).
>
> Signed-off-by: Christian Eggers <[email protected]>
> Co-developed-by: Arun Ramadoss <[email protected]>
> Signed-off-by: Arun Ramadoss <[email protected]>
> ---
> v1 -> v2
> - Fixed compilation issue when PTP_CLASSIFY not selected in menuconfig
> as reported by kernel test robot <[email protected]>
> ---
> include/linux/ptp_classify.h | 71 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 71 insertions(+)
>
> diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h
> index 2b6ea36ad162..6e5869c2504c 100644
> --- a/include/linux/ptp_classify.h
> +++ b/include/linux/ptp_classify.h
> @@ -10,8 +10,12 @@
> #ifndef _PTP_CLASSIFY_H_
> #define _PTP_CLASSIFY_H_
>
> +#include <asm/unaligned.h>
> #include <linux/ip.h>
> +#include <linux/ktime.h>
> #include <linux/skbuff.h>
> +#include <linux/udp.h>
> +#include <net/checksum.h>
>
> #define PTP_CLASS_NONE 0x00 /* not a PTP event message */
> #define PTP_CLASS_V1 0x01 /* protocol version 1 */
> @@ -129,6 +133,67 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr,
> return msgtype;
> }
>
> +/**
> + * ptp_check_diff8 - Computes new checksum (when altering a 64-bit field)
> + * @old: old field value
> + * @new: new field value
> + * @oldsum: previous checksum
> + *
> + * This function can be used to calculate a new checksum when only a single
> + * field is changed. Similar as ip_vs_check_diff*() in ip_vs.h.
> + *
> + * Return: Updated checksum
> + */
> +static inline __wsum ptp_check_diff8(__be64 old, __be64 new, __wsum oldsum)
> +{
> + __be64 diff[2] = { ~old, new };
> +
> + return csum_partial(diff, sizeof(diff), oldsum);
> +}
> +
> +/**
> + * ptp_header_update_correction - Update PTP header's correction field
> + * @skb: packet buffer
> + * @type: type of the packet (see ptp_classify_raw())
> + * @hdr: ptp header
> + * @correction: new correction value
> + *
> + * This updates the correction field of a PTP header and updates the UDP
> + * checksum (if UDP is used as transport). It is needed for hardware capable of
> + * one-step P2P that does not already modify the correction field of Pdelay_Req
> + * event messages on ingress.
> + */
> +static inline
> +void ptp_header_update_correction(struct sk_buff *skb, unsigned int type,
> + struct ptp_header *hdr, s64 correction)
> +{
> + __be64 correction_old;
> + struct udphdr *uhdr;
> +
> + /* previous correction value is required for checksum update. */
> + memcpy(&correction_old, &hdr->correction, sizeof(correction_old));
> +
> + /* write new correction value */
> + put_unaligned_be64((u64)correction, &hdr->correction);
> +
> + switch (type & PTP_CLASS_PMASK) {
> + case PTP_CLASS_IPV4:
> + case PTP_CLASS_IPV6:
> + /* locate udp header */
> + uhdr = (struct udphdr *)((char *)hdr - sizeof(struct udphdr));
> + break;
> + default:
> + return;
> + }
> +
> + /* update checksum */
> + uhdr->check = csum_fold(ptp_check_diff8(correction_old,
> + hdr->correction,
> + ~csum_unfold(uhdr->check)));
> + if (!uhdr->check)
> + uhdr->check = CSUM_MANGLED_0;
AFAICS the above works under the assumption that skb->ip_summed !=
CHECKSUM_COMPLETE, and such assumption is true for the existing DSA
devices.
Still the new helper is a generic one, so perhaps it should take care
of CHECKSUM_COMPLETE, too? Or at least add a big fat warning in the
helper documentation and/or a warn_on_once(CHECKSUM_COMPLETE).
Thanks!
Paolo
On Thu, 2023-01-05 at 11:09 +0100, Paolo Abeni wrote:
> Hi,
>
> On Wed, 2023-01-04 at 14:13 +0530, Arun Ramadoss wrote:
> > From: Christian Eggers <[email protected]>
> >
> > For P2P delay measurement, the ingress time stamp of the PDelay_Req is
> > required for the correction field of the PDelay_Resp. The application
> > echoes back the correction field of the PDelay_Req when sending the
> > PDelay_Resp.
> >
> > Some hardware (like the ZHAW InES PTP time stamping IP core) subtracts
> > the ingress timestamp autonomously from the correction field, so that
> > the hardware only needs to add the egress timestamp on tx. Other
> > hardware (like the Microchip KSZ9563) reports the ingress time stamp via
> > an interrupt and requires that the software provides this time stamp via
> > tail-tag on tx.
> >
> > In order to avoid introducing a further application interface for this,
> > the driver can simply emulate the behavior of the InES device and
> > subtract the ingress time stamp in software from the correction field.
> >
> > On egress, the correction field can either be kept as it is (and the
> > time stamp field in the tail-tag is set to zero) or move the value from
> > the correction field back to the tail-tag.
> >
> > Changing the correction field requires updating the UDP checksum (if UDP
> > is used as transport).
> >
> > Signed-off-by: Christian Eggers <[email protected]>
> > Co-developed-by: Arun Ramadoss <[email protected]>
> > Signed-off-by: Arun Ramadoss <[email protected]>
> > ---
> > v1 -> v2
> > - Fixed compilation issue when PTP_CLASSIFY not selected in menuconfig
> > as reported by kernel test robot <[email protected]>
> > ---
> > include/linux/ptp_classify.h | 71 ++++++++++++++++++++++++++++++++++++
> > 1 file changed, 71 insertions(+)
> >
> > diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h
> > index 2b6ea36ad162..6e5869c2504c 100644
> > --- a/include/linux/ptp_classify.h
> > +++ b/include/linux/ptp_classify.h
> > @@ -10,8 +10,12 @@
> > #ifndef _PTP_CLASSIFY_H_
> > #define _PTP_CLASSIFY_H_
> >
> > +#include <asm/unaligned.h>
> > #include <linux/ip.h>
> > +#include <linux/ktime.h>
> > #include <linux/skbuff.h>
> > +#include <linux/udp.h>
> > +#include <net/checksum.h>
> >
> > #define PTP_CLASS_NONE 0x00 /* not a PTP event message */
> > #define PTP_CLASS_V1 0x01 /* protocol version 1 */
> > @@ -129,6 +133,67 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr,
> > return msgtype;
> > }
> >
> > +/**
> > + * ptp_check_diff8 - Computes new checksum (when altering a 64-bit field)
> > + * @old: old field value
> > + * @new: new field value
> > + * @oldsum: previous checksum
> > + *
> > + * This function can be used to calculate a new checksum when only a single
> > + * field is changed. Similar as ip_vs_check_diff*() in ip_vs.h.
> > + *
> > + * Return: Updated checksum
> > + */
> > +static inline __wsum ptp_check_diff8(__be64 old, __be64 new, __wsum oldsum)
> > +{
> > + __be64 diff[2] = { ~old, new };
> > +
> > + return csum_partial(diff, sizeof(diff), oldsum);
> > +}
> > +
> > +/**
> > + * ptp_header_update_correction - Update PTP header's correction field
> > + * @skb: packet buffer
> > + * @type: type of the packet (see ptp_classify_raw())
> > + * @hdr: ptp header
> > + * @correction: new correction value
> > + *
> > + * This updates the correction field of a PTP header and updates the UDP
> > + * checksum (if UDP is used as transport). It is needed for hardware capable of
> > + * one-step P2P that does not already modify the correction field of Pdelay_Req
> > + * event messages on ingress.
> > + */
> > +static inline
> > +void ptp_header_update_correction(struct sk_buff *skb, unsigned int type,
> > + struct ptp_header *hdr, s64 correction)
> > +{
> > + __be64 correction_old;
> > + struct udphdr *uhdr;
> > +
> > + /* previous correction value is required for checksum update. */
> > + memcpy(&correction_old, &hdr->correction, sizeof(correction_old));
> > +
> > + /* write new correction value */
> > + put_unaligned_be64((u64)correction, &hdr->correction);
> > +
> > + switch (type & PTP_CLASS_PMASK) {
> > + case PTP_CLASS_IPV4:
> > + case PTP_CLASS_IPV6:
> > + /* locate udp header */
> > + uhdr = (struct udphdr *)((char *)hdr - sizeof(struct udphdr));
> > + break;
> > + default:
> > + return;
> > + }
> > +
> > + /* update checksum */
> > + uhdr->check = csum_fold(ptp_check_diff8(correction_old,
> > + hdr->correction,
> > + ~csum_unfold(uhdr->check)));
> > + if (!uhdr->check)
> > + uhdr->check = CSUM_MANGLED_0;
>
> AFAICS the above works under the assumption that skb->ip_summed !=
> CHECKSUM_COMPLETE, and such assumption is true for the existing DSA
> devices.
>
> Still the new helper is a generic one, so perhaps it should take care
> of CHECKSUM_COMPLETE, too? Or at least add a big fat warning in the
> helper documentation and/or a warn_on_once(CHECKSUM_COMPLETE).
I see this helper is used later even in the tx path, so even packet
with ip_summed == CHECKSUM_PARTIAL could reach here and should be
accomodated accordingly.
Thanks,
Paolo
On Thu, Jan 5, 2023 at 11:09 AM Paolo Abeni <[email protected]> wrote:
>
> Hi,
>
> On Wed, 2023-01-04 at 14:13 +0530, Arun Ramadoss wrote:
> > From: Christian Eggers <[email protected]>
> >
> > For P2P delay measurement, the ingress time stamp of the PDelay_Req is
> > required for the correction field of the PDelay_Resp. The application
> > echoes back the correction field of the PDelay_Req when sending the
> > PDelay_Resp.
> >
> > Some hardware (like the ZHAW InES PTP time stamping IP core) subtracts
> > the ingress timestamp autonomously from the correction field, so that
> > the hardware only needs to add the egress timestamp on tx. Other
> > hardware (like the Microchip KSZ9563) reports the ingress time stamp via
> > an interrupt and requires that the software provides this time stamp via
> > tail-tag on tx.
> >
> > In order to avoid introducing a further application interface for this,
> > the driver can simply emulate the behavior of the InES device and
> > subtract the ingress time stamp in software from the correction field.
> >
> > On egress, the correction field can either be kept as it is (and the
> > time stamp field in the tail-tag is set to zero) or move the value from
> > the correction field back to the tail-tag.
> >
> > Changing the correction field requires updating the UDP checksum (if UDP
> > is used as transport).
> >
> > Signed-off-by: Christian Eggers <[email protected]>
> > Co-developed-by: Arun Ramadoss <[email protected]>
> > Signed-off-by: Arun Ramadoss <[email protected]>
> > ---
> > v1 -> v2
> > - Fixed compilation issue when PTP_CLASSIFY not selected in menuconfig
> > as reported by kernel test robot <[email protected]>
> > ---
> > include/linux/ptp_classify.h | 71 ++++++++++++++++++++++++++++++++++++
> > 1 file changed, 71 insertions(+)
> >
> > diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h
> > index 2b6ea36ad162..6e5869c2504c 100644
> > --- a/include/linux/ptp_classify.h
> > +++ b/include/linux/ptp_classify.h
> > @@ -10,8 +10,12 @@
> > #ifndef _PTP_CLASSIFY_H_
> > #define _PTP_CLASSIFY_H_
> >
> > +#include <asm/unaligned.h>
> > #include <linux/ip.h>
> > +#include <linux/ktime.h>
> > #include <linux/skbuff.h>
> > +#include <linux/udp.h>
> > +#include <net/checksum.h>
> >
> > #define PTP_CLASS_NONE 0x00 /* not a PTP event message */
> > #define PTP_CLASS_V1 0x01 /* protocol version 1 */
> > @@ -129,6 +133,67 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr,
> > return msgtype;
> > }
> >
> > +/**
> > + * ptp_check_diff8 - Computes new checksum (when altering a 64-bit field)
> > + * @old: old field value
> > + * @new: new field value
> > + * @oldsum: previous checksum
> > + *
> > + * This function can be used to calculate a new checksum when only a single
> > + * field is changed. Similar as ip_vs_check_diff*() in ip_vs.h.
> > + *
> > + * Return: Updated checksum
> > + */
> > +static inline __wsum ptp_check_diff8(__be64 old, __be64 new, __wsum oldsum)
> > +{
> > + __be64 diff[2] = { ~old, new };
> > +
> > + return csum_partial(diff, sizeof(diff), oldsum);
> > +}
> > +
> > +/**
> > + * ptp_header_update_correction - Update PTP header's correction field
> > + * @skb: packet buffer
> > + * @type: type of the packet (see ptp_classify_raw())
> > + * @hdr: ptp header
> > + * @correction: new correction value
> > + *
> > + * This updates the correction field of a PTP header and updates the UDP
> > + * checksum (if UDP is used as transport). It is needed for hardware capable of
> > + * one-step P2P that does not already modify the correction field of Pdelay_Req
> > + * event messages on ingress.
> > + */
> > +static inline
> > +void ptp_header_update_correction(struct sk_buff *skb, unsigned int type,
> > + struct ptp_header *hdr, s64 correction)
> > +{
> > + __be64 correction_old;
> > + struct udphdr *uhdr;
> > +
> > + /* previous correction value is required for checksum update. */
> > + memcpy(&correction_old, &hdr->correction, sizeof(correction_old));
> > +
> > + /* write new correction value */
> > + put_unaligned_be64((u64)correction, &hdr->correction);
> > +
> > + switch (type & PTP_CLASS_PMASK) {
> > + case PTP_CLASS_IPV4:
> > + case PTP_CLASS_IPV6:
> > + /* locate udp header */
> > + uhdr = (struct udphdr *)((char *)hdr - sizeof(struct udphdr));
> > + break;
> > + default:
> > + return;
> > + }
> > +
> > + /* update checksum */
> > + uhdr->check = csum_fold(ptp_check_diff8(correction_old,
> > + hdr->correction,
> > + ~csum_unfold(uhdr->check)));
> > + if (!uhdr->check)
> > + uhdr->check = CSUM_MANGLED_0;
>
> AFAICS the above works under the assumption that skb->ip_summed !=
> CHECKSUM_COMPLETE, and such assumption is true for the existing DSA
> devices.
Presumably skb->ip_summed could be forced to CHECKSUM_NONE
Note: if IPV4 UDP checksum is zero, we are not supposed to change it.
(Not sure if this point is already checked in caller)
>
> Still the new helper is a generic one, so perhaps it should take care
> of CHECKSUM_COMPLETE, too? Or at least add a big fat warning in the
> helper documentation and/or a warn_on_once(CHECKSUM_COMPLETE).
>
> Thanks!
>
> Paolo
>
Hi Paolo,
Thanks for the review comment.
On Thu, 2023-01-05 at 11:49 +0100, Paolo Abeni wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
>
>
> > > +/**
> > > + * ptp_header_update_correction - Update PTP header's correction
> > > field
> > > + * @skb: packet buffer
> > > + * @type: type of the packet (see ptp_classify_raw())
> > > + * @hdr: ptp header
> > > + * @correction: new correction value
> > > + *
> > > + * This updates the correction field of a PTP header and updates
> > > the UDP
> > > + * checksum (if UDP is used as transport). It is needed for
> > > hardware capable of
> > > + * one-step P2P that does not already modify the correction
> > > field of Pdelay_Req
> > > + * event messages on ingress.
> > > + */
> > > +static inline
> > > +void ptp_header_update_correction(struct sk_buff *skb, unsigned
> > > int type,
> > > + struct ptp_header *hdr, s64
> > > correction)
> > > +{
> > > + __be64 correction_old;
> > > + struct udphdr *uhdr;
> > > +
> > > + /* previous correction value is required for checksum update.
> > > */
> > > + memcpy(&correction_old, &hdr->correction,
> > > sizeof(correction_old));
> > > +
> > > + /* write new correction value */
> > > + put_unaligned_be64((u64)correction, &hdr->correction);
> > > +
> > > + switch (type & PTP_CLASS_PMASK) {
> > > + case PTP_CLASS_IPV4:
> > > + case PTP_CLASS_IPV6:
> > > + /* locate udp header */
> > > + uhdr = (struct udphdr *)((char *)hdr - sizeof(struct
> > > udphdr));
> > > + break;
> > > + default:
> > > + return;
> > > + }
> > > +
> > > + /* update checksum */
> > > + uhdr->check = csum_fold(ptp_check_diff8(correction_old,
> > > + hdr->correction,
> > > + ~csum_unfold(uhdr-
> > > >check)));
> > > + if (!uhdr->check)
> > > + uhdr->check = CSUM_MANGLED_0;
> >
> > AFAICS the above works under the assumption that skb->ip_summed !=
> > CHECKSUM_COMPLETE, and such assumption is true for the existing DSA
> > devices.
> >
> > Still the new helper is a generic one, so perhaps it should take
> > care
> > of CHECKSUM_COMPLETE, too? Or at least add a big fat warning in the
> > helper documentation and/or a warn_on_once(CHECKSUM_COMPLETE).
>
> I see this helper is used later even in the tx path, so even packet
> with ip_summed == CHECKSUM_PARTIAL could reach here and should be
> accomodated accordingly.
Do I need to update the checksum only if ip_sum is not equal to
CHECKSUM_COMPLETE or CHECKSUM_PARTIAL.
if ( skb->ip_summed == CHECKSUM_COMPLETE ||
skb->ip_summed == CHECKSUM_PARTIAL) {
warn_on_once(1);
return;
}
Kindly suggest.
>
> Thanks,
>
> Paolo
>
>
Hi Eric,
On Thu, 2023-01-05 at 12:27 +0100, Eric Dumazet wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
>
> On Thu, Jan 5, 2023 at 11:09 AM Paolo Abeni <[email protected]>
> wrote:
> >
> >
> > > +static inline
> > > +void ptp_header_update_correction(struct sk_buff *skb, unsigned
> > > int type,
> > > + struct ptp_header *hdr, s64
> > > correction)
> > > +{
> > > + __be64 correction_old;
> > > + struct udphdr *uhdr;
> > > +
> > > + /* previous correction value is required for checksum
> > > update. */
> > > + memcpy(&correction_old, &hdr->correction,
> > > sizeof(correction_old));
> > > +
> > > + /* write new correction value */
> > > + put_unaligned_be64((u64)correction, &hdr->correction);
> > > +
> > > + switch (type & PTP_CLASS_PMASK) {
> > > + case PTP_CLASS_IPV4:
> > > + case PTP_CLASS_IPV6:
> > > + /* locate udp header */
> > > + uhdr = (struct udphdr *)((char *)hdr -
> > > sizeof(struct udphdr));
> > > + break;
> > > + default:
> > > + return;
> > > + }
> > > +
> > > + /* update checksum */
> > > + uhdr->check = csum_fold(ptp_check_diff8(correction_old,
> > > + hdr->correction,
> > > + ~csum_unfold(uhdr-
> > > >check)));
> > > + if (!uhdr->check)
> > > + uhdr->check = CSUM_MANGLED_0;
> >
> > AFAICS the above works under the assumption that skb->ip_summed !=
> > CHECKSUM_COMPLETE, and such assumption is true for the existing DSA
> > devices.
>
> Presumably skb->ip_summed could be forced to CHECKSUM_NONE
>
> Note: if IPV4 UDP checksum is zero, we are not supposed to change it.
>
> (Not sure if this point is already checked in caller)
This function is called only for the Pdelay_Req/Resp packet processing
from the hardware where correction field is updated & checksum is
recomputed.
As per the recommendation, Can I set the skb->ip_summed = CHECKSUM_NONE
in the function after recomputing the checksum and resubmit the patch.
Kindly suggest.
>
> >
> > Still the new helper is a generic one, so perhaps it should take
> > care
> > of CHECKSUM_COMPLETE, too? Or at least add a big fat warning in the
> > helper documentation and/or a warn_on_once(CHECKSUM_COMPLETE).
> >
> > Thanks!
> >
> > Paolo
> >