diff mbox series

[RFC,net-next,v3,13/21] ethtool: provide timestamping information in GET_INFO request

Message ID 790ee93d90931278b6e4fc51da712aadfcb8ec90.1550513384.git.mkubecek@suse.cz
State RFC
Delegated to: David Miller
Headers show
Series ethtool netlink interface, part 1 | expand

Commit Message

Michal Kubecek Feb. 18, 2019, 6:22 p.m. UTC
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 <mkubecek@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(-)

Comments

Jakub Kicinski Feb. 20, 2019, 3 a.m. UTC | #1
On Mon, 18 Feb 2019 19:22:29 +0100 (CET), Michal Kubecek wrote:
> 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.

What's the reason for providing the symbolic names?

> Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Michal Kubecek Feb. 20, 2019, 1 p.m. UTC | #2
On Tue, Feb 19, 2019 at 07:00:48PM -0800, Jakub Kicinski wrote:
> On Mon, 18 Feb 2019 19:22:29 +0100 (CET), Michal Kubecek wrote:
> > 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.
> 
> What's the reason for providing the symbolic names?

One of the the goals I had was to reduce the need to keep the lists of
possible values in sync between kernel and userspace ethtool and other
users of the interface so that when a new value is added, we don't have
to update all userspace tools to be able to use or present it.

This already works in ethtool for some newer commands (e.g. features)
and obviously for those where the list of available options depends on
the device (e.g. private flags or statistics). I would like to extend
the principle also to older commands and new ones which do not work like
this (e.g. device reset).

Michal
Jakub Kicinski Feb. 20, 2019, 6:37 p.m. UTC | #3
On Wed, 20 Feb 2019 14:00:07 +0100, Michal Kubecek wrote:
> On Tue, Feb 19, 2019 at 07:00:48PM -0800, Jakub Kicinski wrote:
> > On Mon, 18 Feb 2019 19:22:29 +0100 (CET), Michal Kubecek wrote:  
> > > 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.  
> > 
> > What's the reason for providing the symbolic names?  
> 
> One of the the goals I had was to reduce the need to keep the lists of
> possible values in sync between kernel and userspace ethtool and other
> users of the interface so that when a new value is added, we don't have
> to update all userspace tools to be able to use or present it.
> 
> This already works in ethtool for some newer commands (e.g. features)
> and obviously for those where the list of available options depends on
> the device (e.g. private flags or statistics). I would like to extend
> the principle also to older commands and new ones which do not work like
> this (e.g. device reset).

Let me try to argue that's the wrong direction.  People should learn to
update their user space tooling if they want access to new features.  
In my (limited) experience trying to solve forward compatibility leads
to short term gains, and long term warts in the APIs and increased
maintenance burden in the kernel.
diff mbox series

Patch

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 {