diff options
-rw-r--r-- | Documentation/netlink/specs/ethtool.yaml | 3 | ||||
-rw-r--r-- | Documentation/networking/ethtool-netlink.rst | 16 | ||||
-rw-r--r-- | drivers/net/dsa/ocelot/felix.c | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot_net.c | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot_ptp.c | 53 | ||||
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot_stats.c | 37 | ||||
-rw-r--r-- | include/linux/ethtool.h | 7 | ||||
-rw-r--r-- | include/net/dsa.h | 2 | ||||
-rw-r--r-- | include/soc/mscc/ocelot.h | 11 | ||||
-rw-r--r-- | include/uapi/linux/ethtool_netlink_generated.h | 1 | ||||
-rw-r--r-- | net/dsa/user.c | 11 | ||||
-rw-r--r-- | net/ethtool/tsinfo.c | 2 |
12 files changed, 147 insertions, 16 deletions
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml index 66be04013048..259cb211a338 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -842,6 +842,9 @@ attribute-sets: - name: tx-err type: uint + - + name: tx-onestep-pkts-unconfirmed + type: uint - name: ts-hwtstamp-provider attr-cnt-name: __ethtool-a-ts-hwtstamp-provider-cnt diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index f70c0249860c..3770a2294509 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -1281,11 +1281,17 @@ would be empty (no bit set). Additional hardware timestamping statistics response contents: - ===================================== ====== =================================== - ``ETHTOOL_A_TS_STAT_TX_PKTS`` uint Packets with Tx HW timestamps - ``ETHTOOL_A_TS_STAT_TX_LOST`` uint Tx HW timestamp not arrived count - ``ETHTOOL_A_TS_STAT_TX_ERR`` uint HW error request Tx timestamp count - ===================================== ====== =================================== + ================================================== ====== ===================== + ``ETHTOOL_A_TS_STAT_TX_PKTS`` uint Packets with Tx + HW timestamps + ``ETHTOOL_A_TS_STAT_TX_LOST`` uint Tx HW timestamp + not arrived count + ``ETHTOOL_A_TS_STAT_TX_ERR`` uint HW error request + Tx timestamp count + ``ETHTOOL_A_TS_STAT_TX_ONESTEP_PKTS_UNCONFIRMED`` uint Packets with one-step + HW TX timestamps with + unconfirmed delivery + ================================================== ====== ===================== CABLE_TEST ========== diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 3aa9c997018a..0a4e682a55ef 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1316,6 +1316,14 @@ static void felix_get_eth_phy_stats(struct dsa_switch *ds, int port, ocelot_port_get_eth_phy_stats(ocelot, port, phy_stats); } +static void felix_get_ts_stats(struct dsa_switch *ds, int port, + struct ethtool_ts_stats *ts_stats) +{ + struct ocelot *ocelot = ds->priv; + + ocelot_port_get_ts_stats(ocelot, port, ts_stats); +} + static void felix_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data) { @@ -2237,6 +2245,7 @@ static const struct dsa_switch_ops felix_switch_ops = { .get_stats64 = felix_get_stats64, .get_pause_stats = felix_get_pause_stats, .get_rmon_stats = felix_get_rmon_stats, + .get_ts_stats = felix_get_ts_stats, .get_eth_ctrl_stats = felix_get_eth_ctrl_stats, .get_eth_mac_stats = felix_get_eth_mac_stats, .get_eth_phy_stats = felix_get_eth_phy_stats, diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 8d48468cddd7..7663d196eaf8 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -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, diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c index 808ce8e68d39..cc1088988da0 100644 --- a/drivers/net/ethernet/mscc/ocelot_ptp.c +++ b/drivers/net/ethernet/mscc/ocelot_ptp.c @@ -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); diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c index c018783757fb..545710dadcf5 100644 --- a/drivers/net/ethernet/mscc/ocelot_stats.c +++ b/drivers/net/ethernet/mscc/ocelot_stats.c @@ -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); diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index e4136b0df892..64301ddf2f59 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -559,6 +559,12 @@ struct ethtool_rmon_stats { /** * struct ethtool_ts_stats - HW timestamping statistics * @pkts: Number of packets successfully timestamped by the hardware. + * @onestep_pkts_unconfirmed: Number of PTP packets with one-step TX + * timestamping that were sent, but for which the + * device offers no confirmation whether they made + * it onto the wire and the timestamp was inserted + * in the originTimestamp or correctionField, or + * not. * @lost: Number of hardware timestamping requests where the timestamping * information from the hardware never arrived for submission with * the skb. @@ -571,6 +577,7 @@ struct ethtool_rmon_stats { struct ethtool_ts_stats { struct_group(tx_stats, u64 pkts; + u64 onestep_pkts_unconfirmed; u64 lost; u64 err; ); diff --git a/include/net/dsa.h b/include/net/dsa.h index 9640d5c67f56..a0a9481c52c2 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -906,6 +906,8 @@ struct dsa_switch_ops { void (*get_rmon_stats)(struct dsa_switch *ds, int port, struct ethtool_rmon_stats *rmon_stats, const struct ethtool_rmon_hist_range **ranges); + void (*get_ts_stats)(struct dsa_switch *ds, int port, + struct ethtool_ts_stats *ts_stats); void (*get_stats64)(struct dsa_switch *ds, int port, struct rtnl_link_stats64 *s); void (*get_pause_stats)(struct dsa_switch *ds, int port, diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 2db9ae0575b6..6db7fc9dbaa4 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -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); diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h index 2e17ff348f89..fe24c3459ac0 100644 --- a/include/uapi/linux/ethtool_netlink_generated.h +++ b/include/uapi/linux/ethtool_netlink_generated.h @@ -382,6 +382,7 @@ enum { ETHTOOL_A_TS_STAT_TX_PKTS, ETHTOOL_A_TS_STAT_TX_LOST, ETHTOOL_A_TS_STAT_TX_ERR, + ETHTOOL_A_TS_STAT_TX_ONESTEP_PKTS_UNCONFIRMED, __ETHTOOL_A_TS_STAT_CNT, ETHTOOL_A_TS_STAT_MAX = (__ETHTOOL_A_TS_STAT_CNT - 1) diff --git a/net/dsa/user.c b/net/dsa/user.c index c74f2b2b92de..291ab1b4acc4 100644 --- a/net/dsa/user.c +++ b/net/dsa/user.c @@ -1150,6 +1150,16 @@ dsa_user_get_rmon_stats(struct net_device *dev, ds->ops->get_rmon_stats(ds, dp->index, rmon_stats, ranges); } +static void dsa_user_get_ts_stats(struct net_device *dev, + struct ethtool_ts_stats *ts_stats) +{ + struct dsa_port *dp = dsa_user_to_port(dev); + struct dsa_switch *ds = dp->ds; + + if (ds->ops->get_ts_stats) + ds->ops->get_ts_stats(ds, dp->index, ts_stats); +} + static void dsa_user_net_selftest(struct net_device *ndev, struct ethtool_test *etest, u64 *buf) { @@ -2501,6 +2511,7 @@ static const struct ethtool_ops dsa_user_ethtool_ops = { .get_eth_mac_stats = dsa_user_get_eth_mac_stats, .get_eth_ctrl_stats = dsa_user_get_eth_ctrl_stats, .get_rmon_stats = dsa_user_get_rmon_stats, + .get_ts_stats = dsa_user_get_ts_stats, .set_wol = dsa_user_set_wol, .get_wol = dsa_user_get_wol, .set_eee = dsa_user_set_eee, diff --git a/net/ethtool/tsinfo.c b/net/ethtool/tsinfo.c index 7e495a41aeec..691be6c445b3 100644 --- a/net/ethtool/tsinfo.c +++ b/net/ethtool/tsinfo.c @@ -186,6 +186,8 @@ static int tsinfo_put_stats(struct sk_buff *skb, if (tsinfo_put_stat(skb, stats->tx_stats.pkts, ETHTOOL_A_TS_STAT_TX_PKTS) || + tsinfo_put_stat(skb, stats->tx_stats.onestep_pkts_unconfirmed, + ETHTOOL_A_TS_STAT_TX_ONESTEP_PKTS_UNCONFIRMED) || tsinfo_put_stat(skb, stats->tx_stats.lost, ETHTOOL_A_TS_STAT_TX_LOST) || tsinfo_put_stat(skb, stats->tx_stats.err, |