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: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Kory Maincent
2024-12-12 18:06:44 +01:00
committed by David S. Miller
parent 35f7cad174
commit b9e3f7dc9e
10 changed files with 563 additions and 21 deletions

View File

@@ -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,20 +766,91 @@ 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;
}
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);
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;
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;
@@ -784,6 +858,55 @@ int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info
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);
info->so_timestamping |= SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
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 = { };