ethtool: add interface to read Tx hardware timestamping statistics
authorRahul Rameshbabu <rrameshbabu@nvidia.com>
Wed, 3 Apr 2024 21:28:39 +0000 (14:28 -0700)
committerJakub Kicinski <kuba@kernel.org>
Sat, 6 Apr 2024 05:24:09 +0000 (22:24 -0700)
Multiple network devices that support hardware timestamping appear to have
common behavior with regards to timestamp handling. Implement common Tx
hardware timestamping statistics in a tx_stats struct_group. Common Rx
hardware timestamping statistics can subsequently be implemented in a
rx_stats struct_group for ethtool_ts_stats.

Signed-off-by: Rahul Rameshbabu <rrameshbabu@nvidia.com>
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Link: https://lore.kernel.org/r/20240403212931.128541-2-rrameshbabu@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/netlink/specs/ethtool.yaml
Documentation/networking/ethtool-netlink.rst
include/linux/ethtool.h
include/uapi/linux/ethtool_netlink.h
net/ethtool/tsinfo.c

index d0e4a47e0f214bf8d47dcacbaa34c4f80ba8877f..b76916394ec93f4265a66b1ebf89671cccb041e7 100644 (file)
@@ -565,6 +565,18 @@ attribute-sets:
       -
         name: tx-lpi-timer
         type: u32
+  -
+    name: ts-stat
+    attributes:
+      -
+        name: tx-pkts
+        type: uint
+      -
+        name: tx-lost
+        type: uint
+      -
+        name: tx-err
+        type: uint
   -
     name: tsinfo
     attributes:
@@ -587,6 +599,10 @@ attribute-sets:
       -
         name: phc-index
         type: u32
+      -
+        name: stats
+        type: nest
+        nested-attributes: ts-stat
   -
     name: cable-result
     attributes:
@@ -1394,6 +1410,7 @@ operations:
             - tx-types
             - rx-filters
             - phc-index
+            - stats
       dump: *tsinfo-get-op
     -
       name: cable-test-act
index d583d9abf2f80698a7f4ca3ef47ce676e71767e1..08d330b0f50fd2a103eb0999a2680651d6580ebb 100644 (file)
@@ -1237,12 +1237,21 @@ Kernel response contents:
   ``ETHTOOL_A_TSINFO_TX_TYPES``          bitset  supported Tx types
   ``ETHTOOL_A_TSINFO_RX_FILTERS``        bitset  supported Rx filters
   ``ETHTOOL_A_TSINFO_PHC_INDEX``         u32     PTP hw clock index
+  ``ETHTOOL_A_TSINFO_STATS``             nested  HW timestamping statistics
   =====================================  ======  ==========================
 
 ``ETHTOOL_A_TSINFO_PHC_INDEX`` is absent if there is no associated PHC (there
 is no special value for this case). The bitset attributes are omitted if they
 would be empty (no bit set).
 
+Additional hardware timestamping statistics response contents:
+
+  =====================================  ======  ===================================
+  ``ETHTOOL_A_TS_STAT_TX_PKTS``          u64     Packets with Tx HW timestamps
+  ``ETHTOOL_A_TS_STAT_TX_LOST``          u64     Tx HW timestamp not arrived count
+  ``ETHTOOL_A_TS_STAT_TX_ERR``           u64     HW error request Tx timestamp count
+  =====================================  ======  ===================================
+
 CABLE_TEST
 ==========
 
index 9901e563f706ee63374c244204e33a589da044eb..6fd9107d3cc010dd2f1ecdb005c412145c461b6c 100644 (file)
@@ -480,6 +480,26 @@ struct ethtool_rmon_stats {
        );
 };
 
+/**
+ * struct ethtool_ts_stats - HW timestamping statistics
+ * @pkts: Number of packets successfully timestamped by the hardware.
+ * @lost: Number of hardware timestamping requests where the timestamping
+ *     information from the hardware never arrived for submission with
+ *     the skb.
+ * @err: Number of arbitrary timestamp generation error events that the
+ *     hardware encountered, exclusive of @lost statistics. Cases such
+ *     as resource exhaustion, unavailability, firmware errors, and
+ *     detected illogical timestamp values not submitted with the skb
+ *     are inclusive to this counter.
+ */
+struct ethtool_ts_stats {
+       struct_group(tx_stats,
+               u64 pkts;
+               u64 lost;
+               u64 err;
+       );
+};
+
 #define ETH_MODULE_EEPROM_PAGE_LEN     128
 #define ETH_MODULE_MAX_I2C_ADDRESS     0x7f
 
@@ -755,7 +775,10 @@ struct ethtool_rxfh_param {
  * @get_ts_info: Get the time stamping and PTP hardware clock capabilities.
  *     It may be called with RCU, or rtnl or reference on the device.
  *     Drivers supporting transmit time stamps in software should set this to
- *     ethtool_op_get_ts_info().
+ *     ethtool_op_get_ts_info(). Drivers must not zero statistics which they
+ *     don't report. The stats structure is initialized to ETHTOOL_STAT_NOT_SET
+ *     indicating driver does not report statistics.
+ * @get_ts_stats: Query the device hardware timestamping statistics.
  * @get_module_info: Get the size and type of the eeprom contained within
  *     a plug-in module.
  * @get_module_eeprom: Get the eeprom information from the plug-in module
@@ -898,6 +921,8 @@ struct ethtool_ops {
                                 struct ethtool_dump *, void *);
        int     (*set_dump)(struct net_device *, struct ethtool_dump *);
        int     (*get_ts_info)(struct net_device *, struct ethtool_ts_info *);
+       void    (*get_ts_stats)(struct net_device *dev,
+                               struct ethtool_ts_stats *ts_stats);
        int     (*get_module_info)(struct net_device *,
                                   struct ethtool_modinfo *);
        int     (*get_module_eeprom)(struct net_device *,
index accbb1a231df5a8b488eddd8572b038dcd332a2b..708272026d803d225167037fc2960a065520fed1 100644 (file)
@@ -478,12 +478,26 @@ enum {
        ETHTOOL_A_TSINFO_TX_TYPES,                      /* bitset */
        ETHTOOL_A_TSINFO_RX_FILTERS,                    /* bitset */
        ETHTOOL_A_TSINFO_PHC_INDEX,                     /* u32 */
+       ETHTOOL_A_TSINFO_STATS,                         /* nest - _A_TSINFO_STAT */
 
        /* add new constants above here */
        __ETHTOOL_A_TSINFO_CNT,
        ETHTOOL_A_TSINFO_MAX = (__ETHTOOL_A_TSINFO_CNT - 1)
 };
 
+enum {
+       ETHTOOL_A_TS_STAT_UNSPEC,
+
+       ETHTOOL_A_TS_STAT_TX_PKTS,                      /* u64 */
+       ETHTOOL_A_TS_STAT_TX_LOST,                      /* u64 */
+       ETHTOOL_A_TS_STAT_TX_ERR,                       /* u64 */
+
+       /* add new constants above here */
+       __ETHTOOL_A_TS_STAT_CNT,
+       ETHTOOL_A_TS_STAT_MAX = (__ETHTOOL_A_TS_STAT_CNT - 1)
+
+};
+
 /* PHC VCLOCKS */
 
 enum {
index 9daed0aab162a17cbed9bfb945e4da37585d0d06..be2755c8d8fde7a2e74225aed4a276a39d72d0f1 100644 (file)
@@ -13,14 +13,18 @@ struct tsinfo_req_info {
 struct tsinfo_reply_data {
        struct ethnl_reply_data         base;
        struct ethtool_ts_info          ts_info;
+       struct ethtool_ts_stats         stats;
 };
 
 #define TSINFO_REPDATA(__reply_base) \
        container_of(__reply_base, struct tsinfo_reply_data, base)
 
+#define ETHTOOL_TS_STAT_CNT \
+       (__ETHTOOL_A_TS_STAT_CNT - (ETHTOOL_A_TS_STAT_UNSPEC + 1))
+
 const struct nla_policy ethnl_tsinfo_get_policy[] = {
        [ETHTOOL_A_TSINFO_HEADER]               =
-               NLA_POLICY_NESTED(ethnl_header_policy),
+               NLA_POLICY_NESTED(ethnl_header_policy_stats),
 };
 
 static int tsinfo_prepare_data(const struct ethnl_req_info *req_base,
@@ -34,6 +38,12 @@ static int tsinfo_prepare_data(const struct ethnl_req_info *req_base,
        ret = ethnl_ops_begin(dev);
        if (ret < 0)
                return ret;
+       if (req_base->flags & ETHTOOL_FLAG_STATS &&
+           dev->ethtool_ops->get_ts_stats) {
+               ethtool_stats_init((u64 *)&data->stats,
+                                  sizeof(data->stats) / sizeof(u64));
+               dev->ethtool_ops->get_ts_stats(dev, &data->stats);
+       }
        ret = __ethtool_get_ts_info(dev, &data->ts_info);
        ethnl_ops_complete(dev);
 
@@ -79,10 +89,47 @@ static int tsinfo_reply_size(const struct ethnl_req_info *req_base,
        }
        if (ts_info->phc_index >= 0)
                len += nla_total_size(sizeof(u32));     /* _TSINFO_PHC_INDEX */
+       if (req_base->flags & ETHTOOL_FLAG_STATS)
+               len += nla_total_size(0) + /* _TSINFO_STATS */
+                      nla_total_size_64bit(sizeof(u64)) * ETHTOOL_TS_STAT_CNT;
 
        return len;
 }
 
+static int tsinfo_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)
+{
+       if (val == ETHTOOL_STAT_NOT_SET)
+               return 0;
+       if (nla_put_uint(skb, attrtype, val))
+               return -EMSGSIZE;
+       return 0;
+}
+
+static int tsinfo_put_stats(struct sk_buff *skb,
+                           const struct ethtool_ts_stats *stats)
+{
+       struct nlattr *nest;
+
+       nest = nla_nest_start(skb, ETHTOOL_A_TSINFO_STATS);
+       if (!nest)
+               return -EMSGSIZE;
+
+       if (tsinfo_put_stat(skb, stats->tx_stats.pkts,
+                           ETHTOOL_A_TS_STAT_TX_PKTS) ||
+           tsinfo_put_stat(skb, stats->tx_stats.lost,
+                           ETHTOOL_A_TS_STAT_TX_LOST) ||
+           tsinfo_put_stat(skb, stats->tx_stats.err,
+                           ETHTOOL_A_TS_STAT_TX_ERR))
+               goto err_cancel;
+
+       nla_nest_end(skb, nest);
+       return 0;
+
+err_cancel:
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
+}
+
 static int tsinfo_fill_reply(struct sk_buff *skb,
                             const struct ethnl_req_info *req_base,
                             const struct ethnl_reply_data *reply_base)
@@ -119,6 +166,9 @@ static int tsinfo_fill_reply(struct sk_buff *skb,
        if (ts_info->phc_index >= 0 &&
            nla_put_u32(skb, ETHTOOL_A_TSINFO_PHC_INDEX, ts_info->phc_index))
                return -EMSGSIZE;
+       if (req_base->flags & ETHTOOL_FLAG_STATS &&
+           tsinfo_put_stats(skb, &data->stats))
+               return -EMSGSIZE;
 
        return 0;
 }