mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-09 21:04:42 -04:00
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>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
==========
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user