Add timestamping information as provided by ETHTOOL_GET_TS_INFO ioctl command in GET_INFO reply if ETH_INFO_IM_TSINFO flag is set in the request.
Add constants for counts of HWTSTAMP_TX_* and HWTSTAM_FILTER_* constants and provide symbolic names for timestamping related values so that they can be retrieved in GET_STRSET and GET_INFO requests. Signed-off-by: Michal Kubecek <mkube...@suse.cz> --- Documentation/networking/ethtool-netlink.txt | 10 +- include/uapi/linux/ethtool.h | 6 + include/uapi/linux/ethtool_netlink.h | 14 +- include/uapi/linux/net_tstamp.h | 13 ++ net/ethtool/common.c | 24 ++++ net/ethtool/common.h | 2 + net/ethtool/info.c | 138 +++++++++++++++++++ net/ethtool/ioctl.c | 20 +-- net/ethtool/netlink.h | 9 ++ net/ethtool/strset.c | 18 +++ 10 files changed, 234 insertions(+), 20 deletions(-) diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt index 1e615e111262..c6c7475340e2 100644 --- a/Documentation/networking/ethtool-netlink.txt +++ b/Documentation/networking/ethtool-netlink.txt @@ -242,6 +242,11 @@ Kernel response contents: ETHA_INFO_PERMADDR (nested) ETHA_PERMADDR_ADDRESS (binary) permanent HW address ETHA_PERMADDR_TYPE (u16) dev->type + ETHA_INFO_TSINFO (nested) timestamping information + ETHA_TSINFO_TIMESTAMPING (bitset) supported flags + ETHA_TSINFO_PHC_INDEX (u32) associated PHC + ETHA_TSINFO_TX_TYPES (bitset) Tx types + ETHA_TSINFO_RX_FILTERS (bitset) Rx filters The meaning of DRVINFO attributes follows the corresponding fields of ETHTOOL_GDRVINFO response. Second part with various counts and sizes is @@ -253,6 +258,9 @@ There is no separate attribute for permanent address length as the length can be determined from attribute length (nla_len()). ETHA_PERMADDR_TYPE provides net_device::type value to give a hint about what kind of address device has. +ETHA_TSINFO_PHC_INDEX can be unsigned as there is no need for special value; +if no PHC is associated, the attribute is not present. + GET_INFO requests allow dumps. @@ -328,7 +336,7 @@ ETHTOOL_SCHANNELS n/a ETHTOOL_SET_DUMP n/a ETHTOOL_GET_DUMP_FLAG n/a ETHTOOL_GET_DUMP_DATA n/a -ETHTOOL_GET_TS_INFO n/a +ETHTOOL_GET_TS_INFO ETHNL_CMD_GET_INFO ETHTOOL_GMODULEINFO n/a ETHTOOL_GMODULEEEPROM n/a ETHTOOL_GEEE n/a diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 9ea278961d80..1b58637d3a4d 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -563,6 +563,9 @@ struct ethtool_pauseparam { * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS * @ETH_SS_PHY_TUNABLES: PHY tunable names + * @ETH_SS_TSTAMP_SOF: timestamping flag names + * @ETH_SS_TSTAMP_TX_TYPE: timestamping Tx type names + * @ETH_SS_TSTAMP_RX_FILTER: timestamping Rx filter names */ enum ethtool_stringset { ETH_SS_TEST = 0, @@ -574,6 +577,9 @@ enum ethtool_stringset { ETH_SS_TUNABLES, ETH_SS_PHY_STATS, ETH_SS_PHY_TUNABLES, + ETH_SS_TSTAMP_SOF, + ETH_SS_TSTAMP_TX_TYPE, + ETH_SS_TSTAMP_RX_FILTER, ETH_SS_COUNT }; diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index fb756b6a8592..8ab2b7454e81 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -154,8 +154,9 @@ enum { #define ETH_INFO_IM_DRVINFO 0x01 #define ETH_INFO_IM_PERMADDR 0x02 +#define ETH_INFO_IM_TSINFO 0x04 -#define ETH_INFO_IM_ALL 0x03 +#define ETH_INFO_IM_ALL 0x07 enum { ETHA_DRVINFO_UNSPEC, @@ -177,6 +178,17 @@ enum { ETHA_PERMADDR_MAX = (__ETHA_PERMADDR_CNT - 1) }; +enum { + ETHA_TSINFO_UNSPEC, + ETHA_TSINFO_TIMESTAMPING, /* bitset */ + ETHA_TSINFO_PHC_INDEX, /* u32 */ + ETHA_TSINFO_TX_TYPES, /* bitset */ + ETHA_TSINFO_RX_FILTERS, /* bitset */ + + __ETHA_TSINFO_CNT, + ETHA_TSINFO_MAX = (__ETHA_TSINFO_CNT - 1) +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/include/uapi/linux/net_tstamp.h b/include/uapi/linux/net_tstamp.h index e5b39721c6e4..e5b0472c4416 100644 --- a/include/uapi/linux/net_tstamp.h +++ b/include/uapi/linux/net_tstamp.h @@ -30,6 +30,9 @@ enum { SOF_TIMESTAMPING_OPT_STATS = (1<<12), SOF_TIMESTAMPING_OPT_PKTINFO = (1<<13), SOF_TIMESTAMPING_OPT_TX_SWHW = (1<<14), + /* when adding a flag, please update so_timestamping_labels array + * in net/ethtool/info.c + */ SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_TX_SWHW, SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) | @@ -90,6 +93,11 @@ enum hwtstamp_tx_types { * queue. */ HWTSTAMP_TX_ONESTEP_SYNC, + /* when adding a value, please update tstamp_tx_type_labels array + * in net/ethtool/info.c + */ + + HWTSTAMP_TX_LAST = HWTSTAMP_TX_ONESTEP_SYNC }; /* possible values for hwtstamp_config->rx_filter */ @@ -132,6 +140,11 @@ enum hwtstamp_rx_filters { /* NTP, UDP, all versions and packet modes */ HWTSTAMP_FILTER_NTP_ALL, + /* when adding a value, please update tstamp_rx_filter_labels array + * in net/ethtool/info.c + */ + + HWTSTAMP_FILTER_LAST = HWTSTAMP_FILTER_NTP_ALL }; /* SCM_TIMESTAMPING_PKTINFO control message */ diff --git a/net/ethtool/common.c b/net/ethtool/common.c index e0bd7c6c5874..4616816861cc 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note #include <linux/rtnetlink.h> +#include <linux/phy.h> +#include <linux/net_tstamp.h> #include <net/devlink.h> #include "common.h" @@ -135,3 +137,25 @@ int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) return 0; } + +int __ethtool_get_ts_info(struct net_device *dev, struct 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; + + if (phydev && phydev->drv && phydev->drv->ts_info) { + err = phydev->drv->ts_info(phydev, info); + } else if (ops->get_ts_info) { + err = ops->get_ts_info(dev, info); + } else { + info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + info->phc_index = -1; + } + + return err; +} diff --git a/net/ethtool/common.h b/net/ethtool/common.h index e87e58b3a274..02cbee79da35 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -16,4 +16,6 @@ extern const char phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN]; int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); +int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info); + #endif /* _ETHTOOL_COMMON_H */ diff --git a/net/ethtool/info.c b/net/ethtool/info.c index 05dbd87ebc41..838257db1d31 100644 --- a/net/ethtool/info.c +++ b/net/ethtool/info.c @@ -1,15 +1,61 @@ // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +#include <linux/net_tstamp.h> + #include "netlink.h" #include "common.h" #include "bitset.h" +const char *const so_timestamping_labels[] = { + "hardware-transmit", /* SOF_TIMESTAMPING_TX_HARDWARE */ + "software-transmit", /* SOF_TIMESTAMPING_TX_SOFTWARE */ + "hardware-receive", /* SOF_TIMESTAMPING_RX_HARDWARE */ + "software-receive", /* SOF_TIMESTAMPING_RX_SOFTWARE */ + "software-system-clock", /* SOF_TIMESTAMPING_SOFTWARE */ + "hardware-legacy-clock", /* SOF_TIMESTAMPING_SYS_HARDWARE */ + "hardware-raw-clock", /* SOF_TIMESTAMPING_RAW_HARDWARE */ + "option-id", /* SOF_TIMESTAMPING_OPT_ID */ + "sched-transmit", /* SOF_TIMESTAMPING_TX_SCHED */ + "ack-transmit", /* SOF_TIMESTAMPING_TX_ACK */ + "option-cmsg", /* SOF_TIMESTAMPING_OPT_CMSG */ + "option-tsonly", /* SOF_TIMESTAMPING_OPT_TSONLY */ + "option-stats", /* SOF_TIMESTAMPING_OPT_STATS */ + "option-pktinfo", /* SOF_TIMESTAMPING_OPT_PKTINFO */ + "option-tx-swhw", /* SOF_TIMESTAMPING_OPT_TX_SWHW */ +}; + +const char *const tstamp_tx_type_labels[] = { + [HWTSTAMP_TX_OFF] = "off", + [HWTSTAMP_TX_ON] = "on", + [HWTSTAMP_TX_ONESTEP_SYNC] = "one-step-sync", +}; + +const char *const tstamp_rx_filter_labels[] = { + [HWTSTAMP_FILTER_NONE] = "none", + [HWTSTAMP_FILTER_ALL] = "all", + [HWTSTAMP_FILTER_SOME] = "some", + [HWTSTAMP_FILTER_PTP_V1_L4_EVENT] = "ptpv1-l4-event", + [HWTSTAMP_FILTER_PTP_V1_L4_SYNC] = "ptpv1-l4-sync", + [HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ] = "ptpv1-l4-delay-req", + [HWTSTAMP_FILTER_PTP_V2_L4_EVENT] = "ptpv2-l4-event", + [HWTSTAMP_FILTER_PTP_V2_L4_SYNC] = "ptpv2-l4-sync", + [HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ] = "ptpv2-l4-delay-req", + [HWTSTAMP_FILTER_PTP_V2_L2_EVENT] = "ptpv2-l2-event", + [HWTSTAMP_FILTER_PTP_V2_L2_SYNC] = "ptpv2-l2-sync", + [HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ] = "ptpv2-l2-delay-req", + [HWTSTAMP_FILTER_PTP_V2_EVENT] = "ptpv2-event", + [HWTSTAMP_FILTER_PTP_V2_SYNC] = "ptpv2-sync", + [HWTSTAMP_FILTER_PTP_V2_DELAY_REQ] = "ptpv2-delay-req", + [HWTSTAMP_FILTER_NTP_ALL] = "ntp-all", +}; + struct info_data { struct common_req_info reqinfo_base; /* everything below here will be reset for each device in dumps */ struct common_reply_data repdata_base; struct ethtool_drvinfo drvinfo; + struct ethtool_ts_info tsinfo; }; static const struct nla_policy get_info_policy[ETHA_INFO_MAX + 1] = { @@ -19,6 +65,7 @@ static const struct nla_policy get_info_policy[ETHA_INFO_MAX + 1] = { [ETHA_INFO_COMPACT] = { .type = NLA_FLAG }, [ETHA_INFO_DRVINFO] = { .type = NLA_REJECT }, [ETHA_INFO_PERMADDR] = { .type = NLA_REJECT }, + [ETHA_INFO_TSINFO] = { .type = NLA_REJECT }, }; static int parse_info(struct common_req_info *req_info, struct sk_buff *skb, @@ -67,6 +114,11 @@ static int prepare_info(struct common_req_info *req_info, if (ret < 0) req_mask &= ~ETH_INFO_IM_DRVINFO; } + if (req_mask & ETH_INFO_IM_TSINFO) { + ret = __ethtool_get_ts_info(dev, &data->tsinfo); + if (ret < 0) + req_mask &= ~ETH_INFO_IM_TSINFO; + } ethnl_after_ops(dev); data->repdata_base.info_mask = req_mask; @@ -97,6 +149,42 @@ static int permaddr_size(const struct net_device *dev) return nla_total_size(len); } +static int tsinfo_size(const struct ethtool_ts_info *tsinfo, bool compact) +{ + const unsigned int flags = compact ? ETHNL_BITSET_COMPACT : 0; + int len = 0; + int ret; + + /* if any of these exceeds 32, we need a different interface to talk to + * NIC drivers anyway + */ + BUILD_BUG_ON(__SOF_TIMESTAMPING_COUNT > 32); + BUILD_BUG_ON(__HWTSTAMP_TX_COUNT > 32); + BUILD_BUG_ON(__HWTSTAMP_FILTER_COUNT > 32); + + ret = ethnl_bitset32_size(__SOF_TIMESTAMPING_COUNT, + &tsinfo->so_timestamping, NULL, + so_timestamping_labels, flags); + if (ret < 0) + return ret; + len += ret; + ret = ethnl_bitset32_size(__HWTSTAMP_TX_COUNT, + &tsinfo->tx_types, NULL, + tstamp_tx_type_labels, flags); + if (ret < 0) + return ret; + len += ret; + ret = ethnl_bitset32_size(__HWTSTAMP_FILTER_COUNT, + &tsinfo->rx_filters, NULL, + tstamp_rx_filter_labels, flags); + if (ret < 0) + return ret; + len += ret; + len += nla_total_size(sizeof(u32)); + + return nla_total_size(len); +} + static int info_size(const struct common_req_info *req_info) { const struct info_data *data = @@ -110,6 +198,13 @@ static int info_size(const struct common_req_info *req_info) len += drvinfo_size(&data->drvinfo); if (info_mask & ETH_INFO_IM_PERMADDR) len += permaddr_size(dev); + if (info_mask & ETH_INFO_IM_TSINFO) { + int ret = tsinfo_size(&data->tsinfo, req_info->compact); + + if (ret < 0) + return ret; + len += ret; + } return len; } @@ -158,6 +253,44 @@ static int fill_permaddr(struct sk_buff *skb, const struct net_device *dev) return ret; } +static int fill_tsinfo(struct sk_buff *skb, + const struct ethtool_ts_info *tsinfo, bool compact) +{ + const unsigned int flags = compact ? ETHNL_BITSET_COMPACT : 0; + struct nlattr *nest = ethnl_nest_start(skb, ETHA_INFO_TSINFO); + int ret; + + if (!nest) + return -EMSGSIZE; + ret = ethnl_put_bitset32(skb, ETHA_TSINFO_TIMESTAMPING, + __SOF_TIMESTAMPING_COUNT, + &tsinfo->so_timestamping, NULL, + so_timestamping_labels, flags); + if (ret < 0) + goto err; + ret = -EMSGSIZE; + if (tsinfo->phc_index >= 0 && + nla_put_u32(skb, ETHA_TSINFO_PHC_INDEX, tsinfo->phc_index)) + goto err; + + ret = ethnl_put_bitset32(skb, ETHA_TSINFO_TX_TYPES, __HWTSTAMP_TX_COUNT, + &tsinfo->tx_types, NULL, tstamp_tx_type_labels, + flags); + if (ret < 0) + goto err; + ret = ethnl_put_bitset32(skb, ETHA_TSINFO_RX_FILTERS, + __HWTSTAMP_FILTER_COUNT, &tsinfo->rx_filters, + NULL, tstamp_rx_filter_labels, flags); + if (ret < 0) + goto err; + + nla_nest_end(skb, nest); + return 0; +err: + nla_nest_cancel(skb, nest); + return ret; +} + static int fill_info(struct sk_buff *skb, const struct common_req_info *req_info) { @@ -176,6 +309,11 @@ static int fill_info(struct sk_buff *skb, if (ret < 0) return ret; } + if (info_mask & ETH_INFO_IM_TSINFO) { + ret = fill_tsinfo(skb, &data->tsinfo, req_info->compact); + if (ret < 0) + return ret; + } return 0; } diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index c883239001a4..0837849156d3 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2034,28 +2034,12 @@ static int ethtool_get_dump_data(struct net_device *dev, static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr) { - int err = 0; + int err; struct ethtool_ts_info info; - const struct ethtool_ops *ops = dev->ethtool_ops; - struct phy_device *phydev = dev->phydev; - - memset(&info, 0, sizeof(info)); - info.cmd = ETHTOOL_GET_TS_INFO; - - if (phydev && phydev->drv && phydev->drv->ts_info) { - err = phydev->drv->ts_info(phydev, &info); - } else if (ops->get_ts_info) { - err = ops->get_ts_info(dev, &info); - } else { - info.so_timestamping = - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE; - info.phc_index = -1; - } + err = __ethtool_get_ts_info(dev, &info); if (err) return err; - if (copy_to_user(useraddr, &info, sizeof(info))) err = -EFAULT; diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 7141ec71a6d3..82a4c1f398d8 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -7,14 +7,23 @@ #include <linux/netdevice.h> #include <net/genetlink.h> #include <net/sock.h> +#include <linux/net_tstamp.h> #define ETHNL_SET_ERRMSG(info, msg) \ do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0) +#define __SOF_TIMESTAMPING_COUNT (const_ilog2(SOF_TIMESTAMPING_LAST) + 1) +#define __HWTSTAMP_TX_COUNT (HWTSTAMP_TX_LAST + 1) +#define __HWTSTAMP_FILTER_COUNT (HWTSTAMP_FILTER_LAST + 1) + extern u32 ethnl_bcast_seq; extern struct genl_family ethtool_genl_family; +extern const char *const so_timestamping_labels[]; +extern const char *const tstamp_tx_type_labels[]; +extern const char *const tstamp_rx_filter_labels[]; + struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest); int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype); diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c index a7d0ec2865fb..5c74498d9c72 100644 --- a/net/ethtool/strset.c +++ b/net/ethtool/strset.c @@ -67,6 +67,24 @@ static const struct strset_info info_template[] = { .count = ARRAY_SIZE(phy_tunable_strings), .data = { .legacy = phy_tunable_strings }, }, + [ETH_SS_TSTAMP_SOF] = { + .type = ETH_SS_TYPE_SIMPLE, + .per_dev = false, + .count = __SOF_TIMESTAMPING_COUNT, + .data = { .simple = so_timestamping_labels }, + }, + [ETH_SS_TSTAMP_TX_TYPE] = { + .type = ETH_SS_TYPE_SIMPLE, + .per_dev = false, + .count = __HWTSTAMP_TX_COUNT, + .data = { .simple = tstamp_tx_type_labels }, + }, + [ETH_SS_TSTAMP_RX_FILTER] = { + .type = ETH_SS_TYPE_SIMPLE, + .per_dev = false, + .count = __HWTSTAMP_FILTER_COUNT, + .data = { .simple = tstamp_rx_filter_labels }, + }, }; struct strset_data { -- 2.20.1