]> www.infradead.org Git - users/hch/misc.git/commitdiff
net: mscc: ocelot: add TX timestamping statistics
authorVladimir Oltean <vladimir.oltean@nxp.com>
Thu, 16 Jan 2025 10:46:27 +0000 (12:46 +0200)
committerJakub Kicinski <kuba@kernel.org>
Sat, 18 Jan 2025 04:01:10 +0000 (20:01 -0800)
Add an u64 hardware timestamping statistics structure for each ocelot
port. Export a function from the common switch library for reporting
them to ethtool. This is called by the ocelot switchdev front-end for
now.

Note that for the switchdev driver, we report the one-step PTP packets
as unconfirmed, even though in principle, for some transmission
mechanisms like FDMA, we may be able to confirm transmission and bump
the "pkts" counter in ocelot_fdma_tx_cleanup() instead. I don't have
access to hardware which uses the switchdev front-end, and I've kept the
implementation simple.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Jakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20250116104628.123555-4-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mscc/ocelot_net.c
drivers/net/ethernet/mscc/ocelot_ptp.c
drivers/net/ethernet/mscc/ocelot_stats.c
include/soc/mscc/ocelot.h

index 8d48468cddd7cf91fb49ad23a5c57110900160ef..7663d196eaf89bab65928d5bf4f0be1cee71e852 100644 (file)
@@ -993,6 +993,16 @@ static int ocelot_port_get_ts_info(struct net_device *dev,
        return ocelot_get_ts_info(ocelot, port, info);
 }
 
+static void ocelot_port_ts_stats(struct net_device *dev,
+                                struct ethtool_ts_stats *ts_stats)
+{
+       struct ocelot_port_private *priv = netdev_priv(dev);
+       struct ocelot *ocelot = priv->port.ocelot;
+       int port = priv->port.index;
+
+       ocelot_port_get_ts_stats(ocelot, port, ts_stats);
+}
+
 static const struct ethtool_ops ocelot_ethtool_ops = {
        .get_strings            = ocelot_port_get_strings,
        .get_ethtool_stats      = ocelot_port_get_ethtool_stats,
@@ -1000,6 +1010,7 @@ static const struct ethtool_ops ocelot_ethtool_ops = {
        .get_link_ksettings     = phy_ethtool_get_link_ksettings,
        .set_link_ksettings     = phy_ethtool_set_link_ksettings,
        .get_ts_info            = ocelot_port_get_ts_info,
+       .get_ts_stats           = ocelot_port_ts_stats,
 };
 
 static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port,
index 808ce8e68d3937a2f19a34ae6a7086051cfcdabe..cc1088988da0948bd7f6212dbeace5c032383c26 100644 (file)
@@ -680,9 +680,14 @@ static int ocelot_port_queue_ptp_tx_skb(struct ocelot *ocelot, int port,
        skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) {
                if (time_before(OCELOT_SKB_CB(skb)->ptp_tx_time +
                                OCELOT_PTP_TX_TSTAMP_TIMEOUT, jiffies)) {
-                       dev_warn_ratelimited(ocelot->dev,
-                                            "port %d invalidating stale timestamp ID %u which seems lost\n",
-                                            port, OCELOT_SKB_CB(skb)->ts_id);
+                       u64_stats_update_begin(&ocelot_port->ts_stats->syncp);
+                       ocelot_port->ts_stats->lost++;
+                       u64_stats_update_end(&ocelot_port->ts_stats->syncp);
+
+                       dev_dbg_ratelimited(ocelot->dev,
+                                           "port %d invalidating stale timestamp ID %u which seems lost\n",
+                                           port, OCELOT_SKB_CB(skb)->ts_id);
+
                        __skb_unlink(skb, &ocelot_port->tx_skbs);
                        kfree_skb(skb);
                        ocelot->ptp_skbs_in_flight--;
@@ -748,13 +753,20 @@ int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
                return 0;
 
        ptp_class = ptp_classify_raw(skb);
-       if (ptp_class == PTP_CLASS_NONE)
-               return -EINVAL;
+       if (ptp_class == PTP_CLASS_NONE) {
+               err = -EINVAL;
+               goto error;
+       }
 
        /* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */
        if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) {
                if (ocelot_ptp_is_onestep_sync(skb, ptp_class)) {
                        OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
+
+                       u64_stats_update_begin(&ocelot_port->ts_stats->syncp);
+                       ocelot_port->ts_stats->onestep_pkts_unconfirmed++;
+                       u64_stats_update_end(&ocelot_port->ts_stats->syncp);
+
                        return 0;
                }
 
@@ -764,14 +776,16 @@ int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
 
        if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
                *clone = skb_clone_sk(skb);
-               if (!(*clone))
-                       return -ENOMEM;
+               if (!(*clone)) {
+                       err = -ENOMEM;
+                       goto error;
+               }
 
                /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */
                err = ocelot_port_queue_ptp_tx_skb(ocelot, port, *clone);
                if (err) {
                        kfree_skb(*clone);
-                       return err;
+                       goto error;
                }
 
                skb_shinfo(*clone)->tx_flags |= SKBTX_IN_PROGRESS;
@@ -780,6 +794,12 @@ int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
        }
 
        return 0;
+
+error:
+       u64_stats_update_begin(&ocelot_port->ts_stats->syncp);
+       ocelot_port->ts_stats->err++;
+       u64_stats_update_end(&ocelot_port->ts_stats->syncp);
+       return err;
 }
 EXPORT_SYMBOL(ocelot_port_txtstamp_request);
 
@@ -816,6 +836,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
 
        while (budget--) {
                struct skb_shared_hwtstamps shhwtstamps;
+               struct ocelot_port *ocelot_port;
                u32 val, id, seqid, txport;
                struct sk_buff *skb_match;
                struct timespec64 ts;
@@ -832,17 +853,27 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
                id = SYS_PTP_STATUS_PTP_MESS_ID_X(val);
                txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val);
                seqid = SYS_PTP_STATUS_PTP_MESS_SEQ_ID(val);
+               ocelot_port = ocelot->ports[txport];
 
                /* Retrieve its associated skb */
                skb_match = ocelot_port_dequeue_ptp_tx_skb(ocelot, txport, id,
                                                           seqid);
                if (!skb_match) {
-                       dev_warn_ratelimited(ocelot->dev,
-                                            "port %d received TX timestamp (seqid %d, ts id %u) for packet previously declared stale\n",
-                                            txport, seqid, id);
+                       u64_stats_update_begin(&ocelot_port->ts_stats->syncp);
+                       ocelot_port->ts_stats->err++;
+                       u64_stats_update_end(&ocelot_port->ts_stats->syncp);
+
+                       dev_dbg_ratelimited(ocelot->dev,
+                                           "port %d received TX timestamp (seqid %d, ts id %u) for packet previously declared stale\n",
+                                           txport, seqid, id);
+
                        goto next_ts;
                }
 
+               u64_stats_update_begin(&ocelot_port->ts_stats->syncp);
+               ocelot_port->ts_stats->pkts++;
+               u64_stats_update_end(&ocelot_port->ts_stats->syncp);
+
                /* Get the h/w timestamp */
                ocelot_get_hwtimestamp(ocelot, &ts);
 
index c018783757fb2ff76e23c88f7db90d88587cf41e..545710dadcf5449123be70c36258f772e3db336d 100644 (file)
@@ -821,6 +821,26 @@ void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
 }
 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats);
 
+void ocelot_port_get_ts_stats(struct ocelot *ocelot, int port,
+                             struct ethtool_ts_stats *ts_stats)
+{
+       struct ocelot_port *ocelot_port = ocelot->ports[port];
+       struct ocelot_ts_stats *stats = ocelot_port->ts_stats;
+       unsigned int start;
+
+       if (!ocelot->ptp)
+               return;
+
+       do {
+               start = u64_stats_fetch_begin(&stats->syncp);
+               ts_stats->pkts = stats->pkts;
+               ts_stats->onestep_pkts_unconfirmed = stats->onestep_pkts_unconfirmed;
+               ts_stats->lost = stats->lost;
+               ts_stats->err = stats->err;
+       } while (u64_stats_fetch_retry(&stats->syncp, start));
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_ts_stats);
+
 void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
                             struct rtnl_link_stats64 *stats)
 {
@@ -960,6 +980,23 @@ int ocelot_stats_init(struct ocelot *ocelot)
        if (!ocelot->stats)
                return -ENOMEM;
 
+       if (ocelot->ptp) {
+               for (int port = 0; port < ocelot->num_phys_ports; port++) {
+                       struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+                       if (!ocelot_port)
+                               continue;
+
+                       ocelot_port->ts_stats = devm_kzalloc(ocelot->dev,
+                                                            sizeof(*ocelot_port->ts_stats),
+                                                            GFP_KERNEL);
+                       if (!ocelot_port->ts_stats)
+                               return -ENOMEM;
+
+                       u64_stats_init(&ocelot_port->ts_stats->syncp);
+               }
+       }
+
        snprintf(queue_name, sizeof(queue_name), "%s-stats",
                 dev_name(ocelot->dev));
        ocelot->stats_queue = create_singlethread_workqueue(queue_name);
index 2db9ae0575b609d1f682b7a1cb9def0bd55fec69..6db7fc9dbaa49252dab52398d7df7f07bff11b40 100644 (file)
@@ -759,6 +759,14 @@ struct ocelot_mm_state {
        u8 active_preemptible_tcs;
 };
 
+struct ocelot_ts_stats {
+       u64 pkts;
+       u64 onestep_pkts_unconfirmed;
+       u64 lost;
+       u64 err;
+       struct u64_stats_sync syncp;
+};
+
 struct ocelot_port;
 
 struct ocelot_port {
@@ -778,6 +786,7 @@ struct ocelot_port {
 
        phy_interface_t                 phy_mode;
 
+       struct ocelot_ts_stats          *ts_stats;
        struct sk_buff_head             tx_skbs;
 
        unsigned int                    trap_proto;
@@ -1023,6 +1032,8 @@ void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
                                   struct ethtool_eth_mac_stats *mac_stats);
 void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
                                   struct ethtool_eth_phy_stats *phy_stats);
+void ocelot_port_get_ts_stats(struct ocelot *ocelot, int port,
+                             struct ethtool_ts_stats *ts_stats);
 int ocelot_get_ts_info(struct ocelot *ocelot, int port,
                       struct kernel_ethtool_ts_info *info);
 void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);