ethtool: add a new command for getting PHC virtual clocks

Add an interface for getting PHC (PTP Hardware Clock)
virtual clocks, which are based on PHC physical clock
providing hardware timestamp to network packets.

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Yangbo Lu
2021-06-30 16:11:56 +08:00
committed by David S. Miller
parent acb288e804
commit c156174a67
8 changed files with 167 additions and 1 deletions

View File

@@ -7,4 +7,4 @@ obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o
ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \
linkstate.o debug.o wol.o features.o privflags.o rings.o \
channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
tunnels.o fec.o eeprom.o stats.o
tunnels.o fec.o eeprom.o stats.o phc_vclocks.o

View File

@@ -4,6 +4,7 @@
#include <linux/net_tstamp.h>
#include <linux/phy.h>
#include <linux/rtnetlink.h>
#include <linux/ptp_clock_kernel.h>
#include "common.h"
@@ -554,6 +555,18 @@ int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
return 0;
}
int ethtool_get_phc_vclocks(struct net_device *dev, int **vclock_index)
{
struct ethtool_ts_info info = { };
int num = 0;
if (!__ethtool_get_ts_info(dev, &info))
num = ptp_get_vclocks_index(info.phc_index, vclock_index);
return num;
}
EXPORT_SYMBOL(ethtool_get_phc_vclocks);
const struct ethtool_phy_ops *ethtool_phy_ops;
void ethtool_set_ethtool_phy_ops(const struct ethtool_phy_ops *ops)

View File

@@ -248,6 +248,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_TSINFO_GET] = &ethnl_tsinfo_request_ops,
[ETHTOOL_MSG_MODULE_EEPROM_GET] = &ethnl_module_eeprom_request_ops,
[ETHTOOL_MSG_STATS_GET] = &ethnl_stats_request_ops,
[ETHTOOL_MSG_PHC_VCLOCKS_GET] = &ethnl_phc_vclocks_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -958,6 +959,15 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_stats_get_policy,
.maxattr = ARRAY_SIZE(ethnl_stats_get_policy) - 1,
},
{
.cmd = ETHTOOL_MSG_PHC_VCLOCKS_GET,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
.policy = ethnl_phc_vclocks_get_policy,
.maxattr = ARRAY_SIZE(ethnl_phc_vclocks_get_policy) - 1,
},
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {

View File

@@ -347,6 +347,7 @@ extern const struct ethnl_request_ops ethnl_tsinfo_request_ops;
extern const struct ethnl_request_ops ethnl_fec_request_ops;
extern const struct ethnl_request_ops ethnl_module_eeprom_request_ops;
extern const struct ethnl_request_ops ethnl_stats_request_ops;
extern const struct ethnl_request_ops ethnl_phc_vclocks_request_ops;
extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
@@ -382,6 +383,7 @@ extern const struct nla_policy ethnl_fec_get_policy[ETHTOOL_A_FEC_HEADER + 1];
extern const struct nla_policy ethnl_fec_set_policy[ETHTOOL_A_FEC_AUTO + 1];
extern const struct nla_policy ethnl_module_eeprom_get_policy[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS + 1];
extern const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1];
extern const struct nla_policy ethnl_phc_vclocks_get_policy[ETHTOOL_A_PHC_VCLOCKS_HEADER + 1];
int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);

94
net/ethtool/phc_vclocks.c Normal file
View File

@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2021 NXP
*/
#include "netlink.h"
#include "common.h"
struct phc_vclocks_req_info {
struct ethnl_req_info base;
};
struct phc_vclocks_reply_data {
struct ethnl_reply_data base;
int num;
int *index;
};
#define PHC_VCLOCKS_REPDATA(__reply_base) \
container_of(__reply_base, struct phc_vclocks_reply_data, base)
const struct nla_policy ethnl_phc_vclocks_get_policy[] = {
[ETHTOOL_A_PHC_VCLOCKS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
};
static int phc_vclocks_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
struct phc_vclocks_reply_data *data = PHC_VCLOCKS_REPDATA(reply_base);
struct net_device *dev = reply_base->dev;
int ret;
ret = ethnl_ops_begin(dev);
if (ret < 0)
return ret;
data->num = ethtool_get_phc_vclocks(dev, &data->index);
ethnl_ops_complete(dev);
return ret;
}
static int phc_vclocks_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct phc_vclocks_reply_data *data =
PHC_VCLOCKS_REPDATA(reply_base);
int len = 0;
if (data->num > 0) {
len += nla_total_size(sizeof(u32));
len += nla_total_size(sizeof(s32) * data->num);
}
return len;
}
static int phc_vclocks_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct phc_vclocks_reply_data *data =
PHC_VCLOCKS_REPDATA(reply_base);
if (data->num <= 0)
return 0;
if (nla_put_u32(skb, ETHTOOL_A_PHC_VCLOCKS_NUM, data->num) ||
nla_put(skb, ETHTOOL_A_PHC_VCLOCKS_INDEX,
sizeof(s32) * data->num, data->index))
return -EMSGSIZE;
return 0;
}
static void phc_vclocks_cleanup_data(struct ethnl_reply_data *reply_base)
{
const struct phc_vclocks_reply_data *data =
PHC_VCLOCKS_REPDATA(reply_base);
kfree(data->index);
}
const struct ethnl_request_ops ethnl_phc_vclocks_request_ops = {
.request_cmd = ETHTOOL_MSG_PHC_VCLOCKS_GET,
.reply_cmd = ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY,
.hdr_attr = ETHTOOL_A_PHC_VCLOCKS_HEADER,
.req_info_size = sizeof(struct phc_vclocks_req_info),
.reply_data_size = sizeof(struct phc_vclocks_reply_data),
.prepare_data = phc_vclocks_prepare_data,
.reply_size = phc_vclocks_reply_size,
.fill_reply = phc_vclocks_fill_reply,
.cleanup_data = phc_vclocks_cleanup_data,
};