mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02: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:
		
							parent
							
								
									4b0a3ffa79
								
							
						
					
					
						commit
						8fbd24f3d1
					
				
					 4 changed files with 101 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -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,
 | 
			
		||||
			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,
 | 
			
		||||
			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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue