1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

net: mscc: ocelot: add TX timestamping statistics

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>
This commit is contained in:
Vladimir Oltean 2025-01-16 12:46:27 +02:00 committed by Jakub Kicinski
parent 4b0a3ffa79
commit 8fbd24f3d1
4 changed files with 101 additions and 11 deletions

View 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,

View 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);

View 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);

View 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);