Commit b9e3f7dc authored by Kory Maincent's avatar Kory Maincent Committed by David S. Miller
Browse files

net: ethtool: tsinfo: Enhance tsinfo to support several hwtstamp by net topology



Either the MAC or the PHY can provide hwtstamp, so we should be able to
read the tsinfo for any hwtstamp provider.

Enhance 'get' command to retrieve tsinfo of hwtstamp providers within a
network topology.

Add support for a specific dump command to retrieve all hwtstamp
providers within the network topology, with added functionality for
filtered dump to target a single interface.

Signed-off-by: default avatarKory Maincent <kory.maincent@bootlin.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 35f7cad1
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -836,6 +836,20 @@ attribute-sets:
      -
        name: tx-err
        type: uint
  -
    name: ts-hwtstamp-provider
    attr-cnt-name: __ethtool-a-ts-hwtstamp-provider-cnt
    attributes:
      -
        name: unspec
        type: unused
        value: 0
      -
        name: index
        type: u32
      -
        name: qualifier
        type: u32
  -
    name: tsinfo
    attr-cnt-name: __ethtool-a-tsinfo-cnt
@@ -867,6 +881,10 @@ attribute-sets:
        name: stats
        type: nest
        nested-attributes: ts-stat
      -
        name: hwtstamp-provider
        type: nest
        nested-attributes: ts-hwtstamp-provider
  -
    name: cable-result
    attr-cnt-name: __ethtool-a-cable-result-cnt
@@ -1912,6 +1930,7 @@ operations:
        request:
          attributes:
            - header
            - hwtstamp-provider
        reply:
          attributes:
            - header
@@ -1920,6 +1939,7 @@ operations:
            - rx-filters
            - phc-index
            - stats
            - hwtstamp-provider
      dump: *tsinfo-get-op
    -
      name: cable-test-act
+4 −3
Original line number Diff line number Diff line
@@ -1245,9 +1245,10 @@ Gets timestamping information like ``ETHTOOL_GET_TS_INFO`` ioctl request.

Request contents:

  =====================================  ======  ==========================
  ========================================  ======  ============================
  ``ETHTOOL_A_TSINFO_HEADER``               nested  request header
  =====================================  ======  ==========================
  ``ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER``    nested  PTP hw clock provider
  ========================================  ======  ============================

Kernel response contents:

+4 −0
Original line number Diff line number Diff line
@@ -711,6 +711,7 @@ struct ethtool_rxfh_param {
 * @cmd: command number = %ETHTOOL_GET_TS_INFO
 * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags
 * @phc_index: device index of the associated PHC, or -1 if there is none
 * @phc_qualifier: qualifier of the associated PHC
 * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values
 * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values
 */
@@ -718,6 +719,7 @@ struct kernel_ethtool_ts_info {
	u32 cmd;
	u32 so_timestamping;
	int phc_index;
	enum hwtstamp_provider_qualifier phc_qualifier;
	enum hwtstamp_tx_types tx_types;
	enum hwtstamp_rx_filters rx_filters;
};
@@ -749,6 +751,7 @@ struct kernel_ethtool_ts_info {
 *	@rss_context argument to @create_rxfh_context and friends.
 * @supported_coalesce_params: supported types of interrupt coalescing.
 * @supported_ring_params: supported ring params.
 * @supported_hwtstamp_qualifiers: bitfield of supported hwtstamp qualifier.
 * @get_drvinfo: Report driver/device information. Modern drivers no
 *	longer have to implement this callback. Most fields are
 *	correctly filled in by the core using system information, or
@@ -966,6 +969,7 @@ struct ethtool_ops {
	u32	rxfh_max_num_contexts;
	u32	supported_coalesce_params;
	u32	supported_ring_params;
	u32	supported_hwtstamp_qualifiers;
	void	(*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
	int	(*get_regs_len)(struct net_device *);
	void	(*get_regs)(struct net_device *, struct ethtool_regs *, void *);
+10 −0
Original line number Diff line number Diff line
@@ -385,6 +385,15 @@ enum {
	ETHTOOL_A_TS_STAT_MAX = (__ETHTOOL_A_TS_STAT_CNT - 1)
};

enum {
	ETHTOOL_A_TS_HWTSTAMP_PROVIDER_UNSPEC,
	ETHTOOL_A_TS_HWTSTAMP_PROVIDER_INDEX,
	ETHTOOL_A_TS_HWTSTAMP_PROVIDER_QUALIFIER,

	__ETHTOOL_A_TS_HWTSTAMP_PROVIDER_CNT,
	ETHTOOL_A_TS_HWTSTAMP_PROVIDER_MAX = (__ETHTOOL_A_TS_HWTSTAMP_PROVIDER_CNT - 1)
};

enum {
	ETHTOOL_A_TSINFO_UNSPEC,
	ETHTOOL_A_TSINFO_HEADER,
@@ -393,6 +402,7 @@ enum {
	ETHTOOL_A_TSINFO_RX_FILTERS,
	ETHTOOL_A_TSINFO_PHC_INDEX,
	ETHTOOL_A_TSINFO_STATS,
	ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER,

	__ETHTOOL_A_TSINFO_CNT,
	ETHTOOL_A_TSINFO_MAX = (__ETHTOOL_A_TSINFO_CNT - 1)
+132 −9
Original line number Diff line number Diff line
@@ -5,9 +5,12 @@
#include <linux/phy.h>
#include <linux/rtnetlink.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/phy_link_topology.h>

#include "netlink.h"
#include "common.h"
#include "../core/dev.h"


const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
	[NETIF_F_SG_BIT] =               "tx-scatter-gather",
@@ -763,17 +766,113 @@ int ethtool_check_ops(const struct ethtool_ops *ops)
	return 0;
}

int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info)
static void ethtool_init_tsinfo(struct kernel_ethtool_ts_info *info)
{
	const struct ethtool_ops *ops = dev->ethtool_ops;
	struct phy_device *phydev = dev->phydev;
	int err = 0;

	memset(info, 0, sizeof(*info));
	info->cmd = ETHTOOL_GET_TS_INFO;
	info->phc_index = -1;
}

int ethtool_net_get_ts_info_by_phc(struct net_device *dev,
				   struct kernel_ethtool_ts_info *info,
				   struct hwtstamp_provider_desc *hwprov_desc)
{
	const struct ethtool_ops *ops = dev->ethtool_ops;
	int err;

	if (!ops->get_ts_info)
		return -ENODEV;

	/* Does ptp comes from netdev */
	ethtool_init_tsinfo(info);
	info->phc_qualifier = hwprov_desc->qualifier;
	err = ops->get_ts_info(dev, info);
	if (err)
		return err;

	if (info->phc_index == hwprov_desc->index &&
	    net_support_hwtstamp_qualifier(dev, hwprov_desc->qualifier))
		return 0;

	if (phy_is_default_hwtstamp(phydev) && phy_has_tsinfo(phydev))
	return -ENODEV;
}

int
ethtool_phy_get_ts_info_by_phc(struct net_device *dev,
			       struct kernel_ethtool_ts_info *info,
			       struct hwtstamp_provider_desc *hwprov_desc)
{
	int err;

	/* Only precise qualifier is supported in phydev */
	if (hwprov_desc->qualifier != HWTSTAMP_PROVIDER_QUALIFIER_PRECISE)
		return -ENODEV;

	/* Look in the phy topology */
	if (dev->link_topo) {
		struct phy_device_node *pdn;
		unsigned long phy_index;

		xa_for_each(&dev->link_topo->phys, phy_index, pdn) {
			if (!phy_has_tsinfo(pdn->phy))
				continue;

			ethtool_init_tsinfo(info);
			err = phy_ts_info(pdn->phy, info);
			if (err)
				return err;

			if (info->phc_index == hwprov_desc->index)
				return 0;
		}
		return -ENODEV;
	}

	/* Look on the dev->phydev */
	if (phy_has_tsinfo(dev->phydev)) {
		ethtool_init_tsinfo(info);
		err = phy_ts_info(dev->phydev, info);
		if (err)
			return err;

		if (info->phc_index == hwprov_desc->index)
			return 0;
	}

	return -ENODEV;
}

int ethtool_get_ts_info_by_phc(struct net_device *dev,
			       struct kernel_ethtool_ts_info *info,
			       struct hwtstamp_provider_desc *hwprov_desc)
{
	int err;

	err = ethtool_net_get_ts_info_by_phc(dev, info, hwprov_desc);
	if (err == -ENODEV)
		err = ethtool_phy_get_ts_info_by_phc(dev, info, hwprov_desc);

	info->so_timestamping |= SOF_TIMESTAMPING_RX_SOFTWARE |
				 SOF_TIMESTAMPING_SOFTWARE;

	return err;
}

int __ethtool_get_ts_info(struct net_device *dev,
			  struct kernel_ethtool_ts_info *info)
{
	struct hwtstamp_provider *hwprov;

	hwprov = rtnl_dereference(dev->hwprov);
	/* No provider specified, use default behavior */
	if (!hwprov) {
		const struct ethtool_ops *ops = dev->ethtool_ops;
		struct phy_device *phydev = dev->phydev;
		int err = 0;

		ethtool_init_tsinfo(info);
		if (phy_is_default_hwtstamp(phydev) &&
		    phy_has_tsinfo(phydev))
			err = phy_ts_info(phydev, info);
		else if (ops->get_ts_info)
			err = ops->get_ts_info(dev, info);
@@ -784,6 +883,30 @@ int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info
		return err;
	}

	return ethtool_get_ts_info_by_phc(dev, info, &hwprov->desc);
}

bool net_support_hwtstamp_qualifier(struct net_device *dev,
				    enum hwtstamp_provider_qualifier qualifier)
{
	const struct ethtool_ops *ops = dev->ethtool_ops;

	if (!ops)
		return false;

	/* Return true with precise qualifier and with NIC without
	 * qualifier description to not break the old behavior.
	 */
	if (!ops->supported_hwtstamp_qualifiers &&
	    qualifier == HWTSTAMP_PROVIDER_QUALIFIER_PRECISE)
		return true;

	if (ops->supported_hwtstamp_qualifiers & BIT(qualifier))
		return true;

	return false;
}

int ethtool_get_phc_vclocks(struct net_device *dev, int **vclock_index)
{
	struct kernel_ethtool_ts_info info = { };
Loading