Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'ethtool-get_ts_stats-for-dsa-and-ocelot-driver'

Vladimir Oltean says:

====================
ethtool get_ts_stats() for DSA and ocelot driver

After a recent patch set with fixes and general restructuring, Jakub asked
for the Felix DSA driver to start reporting standardized statistics
for hardware timestamping:
https://lore.kernel.org/netdev/20241207180640.12da60ed@kernel.org/

Testing follows the same procedure as in the aforementioned series, with PTP
packet loss induced through taprio:

$ ethtool -I --show-time-stamping swp3
Time stamping parameters for swp3:
Capabilities:
hardware-transmit
software-transmit
hardware-receive
software-receive
software-system-clock
hardware-raw-clock
PTP Hardware Clock: 1
Hardware Transmit Timestamp Modes:
off
on
onestep-sync
Hardware Receive Filter Modes:
none
ptpv2-l4-event
ptpv2-l2-event
ptpv2-event
Statistics:
tx_pkts: 14591
tx_lost: 85
tx_err: 0

v1: https://lore.kernel.org/20241213140852.1254063-1-vladimir.oltean@nxp.com/
====================

Link: https://patch.msgid.link/20250116104628.123555-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+147 -16
+3
Documentation/netlink/specs/ethtool.yaml
··· 842 842 - 843 843 name: tx-err 844 844 type: uint 845 + - 846 + name: tx-onestep-pkts-unconfirmed 847 + type: uint 845 848 - 846 849 name: ts-hwtstamp-provider 847 850 attr-cnt-name: __ethtool-a-ts-hwtstamp-provider-cnt
+11 -5
Documentation/networking/ethtool-netlink.rst
··· 1281 1281 1282 1282 Additional hardware timestamping statistics response contents: 1283 1283 1284 - ===================================== ====== =================================== 1285 - ``ETHTOOL_A_TS_STAT_TX_PKTS`` uint Packets with Tx HW timestamps 1286 - ``ETHTOOL_A_TS_STAT_TX_LOST`` uint Tx HW timestamp not arrived count 1287 - ``ETHTOOL_A_TS_STAT_TX_ERR`` uint HW error request Tx timestamp count 1288 - ===================================== ====== =================================== 1284 + ================================================== ====== ===================== 1285 + ``ETHTOOL_A_TS_STAT_TX_PKTS`` uint Packets with Tx 1286 + HW timestamps 1287 + ``ETHTOOL_A_TS_STAT_TX_LOST`` uint Tx HW timestamp 1288 + not arrived count 1289 + ``ETHTOOL_A_TS_STAT_TX_ERR`` uint HW error request 1290 + Tx timestamp count 1291 + ``ETHTOOL_A_TS_STAT_TX_ONESTEP_PKTS_UNCONFIRMED`` uint Packets with one-step 1292 + HW TX timestamps with 1293 + unconfirmed delivery 1294 + ================================================== ====== ===================== 1289 1295 1290 1296 CABLE_TEST 1291 1297 ==========
+9
drivers/net/dsa/ocelot/felix.c
··· 1316 1316 ocelot_port_get_eth_phy_stats(ocelot, port, phy_stats); 1317 1317 } 1318 1318 1319 + static void felix_get_ts_stats(struct dsa_switch *ds, int port, 1320 + struct ethtool_ts_stats *ts_stats) 1321 + { 1322 + struct ocelot *ocelot = ds->priv; 1323 + 1324 + ocelot_port_get_ts_stats(ocelot, port, ts_stats); 1325 + } 1326 + 1319 1327 static void felix_get_strings(struct dsa_switch *ds, int port, 1320 1328 u32 stringset, u8 *data) 1321 1329 { ··· 2245 2237 .get_stats64 = felix_get_stats64, 2246 2238 .get_pause_stats = felix_get_pause_stats, 2247 2239 .get_rmon_stats = felix_get_rmon_stats, 2240 + .get_ts_stats = felix_get_ts_stats, 2248 2241 .get_eth_ctrl_stats = felix_get_eth_ctrl_stats, 2249 2242 .get_eth_mac_stats = felix_get_eth_mac_stats, 2250 2243 .get_eth_phy_stats = felix_get_eth_phy_stats,
+11
drivers/net/ethernet/mscc/ocelot_net.c
··· 993 993 return ocelot_get_ts_info(ocelot, port, info); 994 994 } 995 995 996 + static void ocelot_port_ts_stats(struct net_device *dev, 997 + struct ethtool_ts_stats *ts_stats) 998 + { 999 + struct ocelot_port_private *priv = netdev_priv(dev); 1000 + struct ocelot *ocelot = priv->port.ocelot; 1001 + int port = priv->port.index; 1002 + 1003 + ocelot_port_get_ts_stats(ocelot, port, ts_stats); 1004 + } 1005 + 996 1006 static const struct ethtool_ops ocelot_ethtool_ops = { 997 1007 .get_strings = ocelot_port_get_strings, 998 1008 .get_ethtool_stats = ocelot_port_get_ethtool_stats, ··· 1010 1000 .get_link_ksettings = phy_ethtool_get_link_ksettings, 1011 1001 .set_link_ksettings = phy_ethtool_set_link_ksettings, 1012 1002 .get_ts_info = ocelot_port_get_ts_info, 1003 + .get_ts_stats = ocelot_port_ts_stats, 1013 1004 }; 1014 1005 1015 1006 static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port,
+42 -11
drivers/net/ethernet/mscc/ocelot_ptp.c
··· 680 680 skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) { 681 681 if (time_before(OCELOT_SKB_CB(skb)->ptp_tx_time + 682 682 OCELOT_PTP_TX_TSTAMP_TIMEOUT, jiffies)) { 683 - dev_warn_ratelimited(ocelot->dev, 684 - "port %d invalidating stale timestamp ID %u which seems lost\n", 685 - port, OCELOT_SKB_CB(skb)->ts_id); 683 + u64_stats_update_begin(&ocelot_port->ts_stats->syncp); 684 + ocelot_port->ts_stats->lost++; 685 + u64_stats_update_end(&ocelot_port->ts_stats->syncp); 686 + 687 + dev_dbg_ratelimited(ocelot->dev, 688 + "port %d invalidating stale timestamp ID %u which seems lost\n", 689 + port, OCELOT_SKB_CB(skb)->ts_id); 690 + 686 691 __skb_unlink(skb, &ocelot_port->tx_skbs); 687 692 kfree_skb(skb); 688 693 ocelot->ptp_skbs_in_flight--; ··· 753 748 return 0; 754 749 755 750 ptp_class = ptp_classify_raw(skb); 756 - if (ptp_class == PTP_CLASS_NONE) 757 - return -EINVAL; 751 + if (ptp_class == PTP_CLASS_NONE) { 752 + err = -EINVAL; 753 + goto error; 754 + } 758 755 759 756 /* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */ 760 757 if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) { 761 758 if (ocelot_ptp_is_onestep_sync(skb, ptp_class)) { 762 759 OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; 760 + 761 + u64_stats_update_begin(&ocelot_port->ts_stats->syncp); 762 + ocelot_port->ts_stats->onestep_pkts_unconfirmed++; 763 + u64_stats_update_end(&ocelot_port->ts_stats->syncp); 764 + 763 765 return 0; 764 766 } 765 767 ··· 776 764 777 765 if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { 778 766 *clone = skb_clone_sk(skb); 779 - if (!(*clone)) 780 - return -ENOMEM; 767 + if (!(*clone)) { 768 + err = -ENOMEM; 769 + goto error; 770 + } 781 771 782 772 /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ 783 773 err = ocelot_port_queue_ptp_tx_skb(ocelot, port, *clone); 784 774 if (err) { 785 775 kfree_skb(*clone); 786 - return err; 776 + goto error; 787 777 } 788 778 789 779 skb_shinfo(*clone)->tx_flags |= SKBTX_IN_PROGRESS; ··· 794 780 } 795 781 796 782 return 0; 783 + 784 + error: 785 + u64_stats_update_begin(&ocelot_port->ts_stats->syncp); 786 + ocelot_port->ts_stats->err++; 787 + u64_stats_update_end(&ocelot_port->ts_stats->syncp); 788 + return err; 797 789 } 798 790 EXPORT_SYMBOL(ocelot_port_txtstamp_request); 799 791 ··· 836 816 837 817 while (budget--) { 838 818 struct skb_shared_hwtstamps shhwtstamps; 819 + struct ocelot_port *ocelot_port; 839 820 u32 val, id, seqid, txport; 840 821 struct sk_buff *skb_match; 841 822 struct timespec64 ts; ··· 853 832 id = SYS_PTP_STATUS_PTP_MESS_ID_X(val); 854 833 txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val); 855 834 seqid = SYS_PTP_STATUS_PTP_MESS_SEQ_ID(val); 835 + ocelot_port = ocelot->ports[txport]; 856 836 857 837 /* Retrieve its associated skb */ 858 838 skb_match = ocelot_port_dequeue_ptp_tx_skb(ocelot, txport, id, 859 839 seqid); 860 840 if (!skb_match) { 861 - dev_warn_ratelimited(ocelot->dev, 862 - "port %d received TX timestamp (seqid %d, ts id %u) for packet previously declared stale\n", 863 - txport, seqid, id); 841 + u64_stats_update_begin(&ocelot_port->ts_stats->syncp); 842 + ocelot_port->ts_stats->err++; 843 + u64_stats_update_end(&ocelot_port->ts_stats->syncp); 844 + 845 + dev_dbg_ratelimited(ocelot->dev, 846 + "port %d received TX timestamp (seqid %d, ts id %u) for packet previously declared stale\n", 847 + txport, seqid, id); 848 + 864 849 goto next_ts; 865 850 } 851 + 852 + u64_stats_update_begin(&ocelot_port->ts_stats->syncp); 853 + ocelot_port->ts_stats->pkts++; 854 + u64_stats_update_end(&ocelot_port->ts_stats->syncp); 866 855 867 856 /* Get the h/w timestamp */ 868 857 ocelot_get_hwtimestamp(ocelot, &ts);
+37
drivers/net/ethernet/mscc/ocelot_stats.c
··· 821 821 } 822 822 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats); 823 823 824 + void ocelot_port_get_ts_stats(struct ocelot *ocelot, int port, 825 + struct ethtool_ts_stats *ts_stats) 826 + { 827 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 828 + struct ocelot_ts_stats *stats = ocelot_port->ts_stats; 829 + unsigned int start; 830 + 831 + if (!ocelot->ptp) 832 + return; 833 + 834 + do { 835 + start = u64_stats_fetch_begin(&stats->syncp); 836 + ts_stats->pkts = stats->pkts; 837 + ts_stats->onestep_pkts_unconfirmed = stats->onestep_pkts_unconfirmed; 838 + ts_stats->lost = stats->lost; 839 + ts_stats->err = stats->err; 840 + } while (u64_stats_fetch_retry(&stats->syncp, start)); 841 + } 842 + EXPORT_SYMBOL_GPL(ocelot_port_get_ts_stats); 843 + 824 844 void ocelot_port_get_stats64(struct ocelot *ocelot, int port, 825 845 struct rtnl_link_stats64 *stats) 826 846 { ··· 979 959 sizeof(u64), GFP_KERNEL); 980 960 if (!ocelot->stats) 981 961 return -ENOMEM; 962 + 963 + if (ocelot->ptp) { 964 + for (int port = 0; port < ocelot->num_phys_ports; port++) { 965 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 966 + 967 + if (!ocelot_port) 968 + continue; 969 + 970 + ocelot_port->ts_stats = devm_kzalloc(ocelot->dev, 971 + sizeof(*ocelot_port->ts_stats), 972 + GFP_KERNEL); 973 + if (!ocelot_port->ts_stats) 974 + return -ENOMEM; 975 + 976 + u64_stats_init(&ocelot_port->ts_stats->syncp); 977 + } 978 + } 982 979 983 980 snprintf(queue_name, sizeof(queue_name), "%s-stats", 984 981 dev_name(ocelot->dev));
+7
include/linux/ethtool.h
··· 559 559 /** 560 560 * struct ethtool_ts_stats - HW timestamping statistics 561 561 * @pkts: Number of packets successfully timestamped by the hardware. 562 + * @onestep_pkts_unconfirmed: Number of PTP packets with one-step TX 563 + * timestamping that were sent, but for which the 564 + * device offers no confirmation whether they made 565 + * it onto the wire and the timestamp was inserted 566 + * in the originTimestamp or correctionField, or 567 + * not. 562 568 * @lost: Number of hardware timestamping requests where the timestamping 563 569 * information from the hardware never arrived for submission with 564 570 * the skb. ··· 577 571 struct ethtool_ts_stats { 578 572 struct_group(tx_stats, 579 573 u64 pkts; 574 + u64 onestep_pkts_unconfirmed; 580 575 u64 lost; 581 576 u64 err; 582 577 );
+2
include/net/dsa.h
··· 906 906 void (*get_rmon_stats)(struct dsa_switch *ds, int port, 907 907 struct ethtool_rmon_stats *rmon_stats, 908 908 const struct ethtool_rmon_hist_range **ranges); 909 + void (*get_ts_stats)(struct dsa_switch *ds, int port, 910 + struct ethtool_ts_stats *ts_stats); 909 911 void (*get_stats64)(struct dsa_switch *ds, int port, 910 912 struct rtnl_link_stats64 *s); 911 913 void (*get_pause_stats)(struct dsa_switch *ds, int port,
+11
include/soc/mscc/ocelot.h
··· 759 759 u8 active_preemptible_tcs; 760 760 }; 761 761 762 + struct ocelot_ts_stats { 763 + u64 pkts; 764 + u64 onestep_pkts_unconfirmed; 765 + u64 lost; 766 + u64 err; 767 + struct u64_stats_sync syncp; 768 + }; 769 + 762 770 struct ocelot_port; 763 771 764 772 struct ocelot_port { ··· 786 778 787 779 phy_interface_t phy_mode; 788 780 781 + struct ocelot_ts_stats *ts_stats; 789 782 struct sk_buff_head tx_skbs; 790 783 791 784 unsigned int trap_proto; ··· 1032 1023 struct ethtool_eth_mac_stats *mac_stats); 1033 1024 void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port, 1034 1025 struct ethtool_eth_phy_stats *phy_stats); 1026 + void ocelot_port_get_ts_stats(struct ocelot *ocelot, int port, 1027 + struct ethtool_ts_stats *ts_stats); 1035 1028 int ocelot_get_ts_info(struct ocelot *ocelot, int port, 1036 1029 struct kernel_ethtool_ts_info *info); 1037 1030 void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);
+11
net/dsa/user.c
··· 1150 1150 ds->ops->get_rmon_stats(ds, dp->index, rmon_stats, ranges); 1151 1151 } 1152 1152 1153 + static void dsa_user_get_ts_stats(struct net_device *dev, 1154 + struct ethtool_ts_stats *ts_stats) 1155 + { 1156 + struct dsa_port *dp = dsa_user_to_port(dev); 1157 + struct dsa_switch *ds = dp->ds; 1158 + 1159 + if (ds->ops->get_ts_stats) 1160 + ds->ops->get_ts_stats(ds, dp->index, ts_stats); 1161 + } 1162 + 1153 1163 static void dsa_user_net_selftest(struct net_device *ndev, 1154 1164 struct ethtool_test *etest, u64 *buf) 1155 1165 { ··· 2511 2501 .get_eth_mac_stats = dsa_user_get_eth_mac_stats, 2512 2502 .get_eth_ctrl_stats = dsa_user_get_eth_ctrl_stats, 2513 2503 .get_rmon_stats = dsa_user_get_rmon_stats, 2504 + .get_ts_stats = dsa_user_get_ts_stats, 2514 2505 .set_wol = dsa_user_set_wol, 2515 2506 .get_wol = dsa_user_get_wol, 2516 2507 .set_eee = dsa_user_set_eee,
+2
net/ethtool/tsinfo.c
··· 186 186 187 187 if (tsinfo_put_stat(skb, stats->tx_stats.pkts, 188 188 ETHTOOL_A_TS_STAT_TX_PKTS) || 189 + tsinfo_put_stat(skb, stats->tx_stats.onestep_pkts_unconfirmed, 190 + ETHTOOL_A_TS_STAT_TX_ONESTEP_PKTS_UNCONFIRMED) || 189 191 tsinfo_put_stat(skb, stats->tx_stats.lost, 190 192 ETHTOOL_A_TS_STAT_TX_LOST) || 191 193 tsinfo_put_stat(skb, stats->tx_stats.err,