ethtool: provide timestamping information with TSINFO_GET request

Implement TSINFO_GET request to get timestamping information for a network
device. This is traditionally available via ETHTOOL_GET_TS_INFO ioctl
request.

Move part of ethtool_get_ts_info() into common.c so that ioctl and netlink
code use the same logic to get timestamping information from the device.

v3: use "TSINFO" rather than "TIMESTAMP", suggested by Richard Cochran

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Michal Kubecek 2020-03-28 00:01:58 +01:00 committed by David S. Miller
parent f76510b458
commit 5b071c59ed
9 changed files with 225 additions and 21 deletions

View file

@ -203,6 +203,7 @@ Userspace to kernel:
``ETHTOOL_MSG_PAUSE_SET`` set pause parameters ``ETHTOOL_MSG_PAUSE_SET`` set pause parameters
``ETHTOOL_MSG_EEE_GET`` get EEE settings ``ETHTOOL_MSG_EEE_GET`` get EEE settings
``ETHTOOL_MSG_EEE_SET`` set EEE settings ``ETHTOOL_MSG_EEE_SET`` set EEE settings
``ETHTOOL_MSG_TSINFO_GET`` get timestamping info
===================================== ================================ ===================================== ================================
Kernel to userspace: Kernel to userspace:
@ -233,6 +234,7 @@ Kernel to userspace:
``ETHTOOL_MSG_PAUSE_NTF`` pause parameters ``ETHTOOL_MSG_PAUSE_NTF`` pause parameters
``ETHTOOL_MSG_EEE_GET_REPLY`` EEE settings ``ETHTOOL_MSG_EEE_GET_REPLY`` EEE settings
``ETHTOOL_MSG_EEE_NTF`` EEE settings ``ETHTOOL_MSG_EEE_NTF`` EEE settings
``ETHTOOL_MSG_TSINFO_GET_REPLY`` timestamping info
===================================== ================================= ===================================== =================================
``GET`` requests are sent by userspace applications to retrieve device ``GET`` requests are sent by userspace applications to retrieve device
@ -928,6 +930,32 @@ but only first 32 can be set at the moment as that is what the ``ethtool_ops``
callback supports. callback supports.
TSINFO_GET
==========
Gets timestamping information like ``ETHTOOL_GET_TS_INFO`` ioctl request.
Request contents:
===================================== ====== ==========================
``ETHTOOL_A_TSINFO_HEADER`` nested request header
===================================== ====== ==========================
Kernel response contents:
===================================== ====== ==========================
``ETHTOOL_A_TSINFO_HEADER`` nested request header
``ETHTOOL_A_TSINFO_TIMESTAMPING`` bitset SO_TIMESTAMPING flags
``ETHTOOL_A_TSINFO_TX_TYPES`` bitset supported Tx types
``ETHTOOL_A_TSINFO_RX_FILTERS`` bitset supported Rx filters
``ETHTOOL_A_TSINFO_PHC_INDEX`` u32 PTP hw clock index
===================================== ====== ==========================
``ETHTOOL_A_TSINFO_PHC_INDEX`` is absent if there is no associated PHC (there
is no special value for this case). The bitset attributes are omitted if they
would be empty (no bit set).
Request translation Request translation
=================== ===================
@ -1003,7 +1031,7 @@ have their netlink replacement yet.
``ETHTOOL_SET_DUMP`` n/a ``ETHTOOL_SET_DUMP`` n/a
``ETHTOOL_GET_DUMP_FLAG`` n/a ``ETHTOOL_GET_DUMP_FLAG`` n/a
``ETHTOOL_GET_DUMP_DATA`` n/a ``ETHTOOL_GET_DUMP_DATA`` n/a
``ETHTOOL_GET_TS_INFO`` n/a ``ETHTOOL_GET_TS_INFO`` ``ETHTOOL_MSG_TSINFO_GET``
``ETHTOOL_GMODULEINFO`` n/a ``ETHTOOL_GMODULEINFO`` n/a
``ETHTOOL_GMODULEEEPROM`` n/a ``ETHTOOL_GMODULEEEPROM`` n/a
``ETHTOOL_GEEE`` ``ETHTOOL_MSG_EEE_GET`` ``ETHTOOL_GEEE`` ``ETHTOOL_MSG_EEE_GET``

View file

@ -38,6 +38,7 @@ enum {
ETHTOOL_MSG_PAUSE_SET, ETHTOOL_MSG_PAUSE_SET,
ETHTOOL_MSG_EEE_GET, ETHTOOL_MSG_EEE_GET,
ETHTOOL_MSG_EEE_SET, ETHTOOL_MSG_EEE_SET,
ETHTOOL_MSG_TSINFO_GET,
/* add new constants above here */ /* add new constants above here */
__ETHTOOL_MSG_USER_CNT, __ETHTOOL_MSG_USER_CNT,
@ -72,6 +73,7 @@ enum {
ETHTOOL_MSG_PAUSE_NTF, ETHTOOL_MSG_PAUSE_NTF,
ETHTOOL_MSG_EEE_GET_REPLY, ETHTOOL_MSG_EEE_GET_REPLY,
ETHTOOL_MSG_EEE_NTF, ETHTOOL_MSG_EEE_NTF,
ETHTOOL_MSG_TSINFO_GET_REPLY,
/* add new constants above here */ /* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT, __ETHTOOL_MSG_KERNEL_CNT,
@ -386,6 +388,21 @@ enum {
ETHTOOL_A_EEE_MAX = (__ETHTOOL_A_EEE_CNT - 1) ETHTOOL_A_EEE_MAX = (__ETHTOOL_A_EEE_CNT - 1)
}; };
/* TSINFO */
enum {
ETHTOOL_A_TSINFO_UNSPEC,
ETHTOOL_A_TSINFO_HEADER, /* nest - _A_HEADER_* */
ETHTOOL_A_TSINFO_TIMESTAMPING, /* bitset */
ETHTOOL_A_TSINFO_TX_TYPES, /* bitset */
ETHTOOL_A_TSINFO_RX_FILTERS, /* bitset */
ETHTOOL_A_TSINFO_PHC_INDEX, /* u32 */
/* add new constants above here */
__ETHTOOL_A_TSINFO_CNT,
ETHTOOL_A_TSINFO_MAX = (__ETHTOOL_A_TSINFO_CNT - 1)
};
/* generic netlink info */ /* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1 #define ETHTOOL_GENL_VERSION 1

View file

@ -6,4 +6,4 @@ obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o
ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.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 \ linkstate.o debug.o wol.o features.o privflags.o rings.o \
channels.o coalesce.o pause.o eee.o channels.o coalesce.o pause.o eee.o tsinfo.o

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
#include <linux/phy.h>
#include "common.h" #include "common.h"
@ -350,3 +351,23 @@ int ethtool_check_ops(const struct ethtool_ops *ops)
*/ */
return 0; 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;
memset(info, 0, sizeof(*info));
info->cmd = ETHTOOL_GET_TS_INFO;
if (phy_has_tsinfo(phydev))
return phy_ts_info(phydev, info);
if (ops->get_ts_info)
return ops->get_ts_info(dev, info);
info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
info->phc_index = -1;
return 0;
}

View file

@ -35,5 +35,6 @@ bool convert_legacy_settings_to_link_ksettings(
struct ethtool_link_ksettings *link_ksettings, struct ethtool_link_ksettings *link_ksettings,
const struct ethtool_cmd *legacy_settings); const struct ethtool_cmd *legacy_settings);
int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max); int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max);
int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info);
#endif /* _ETHTOOL_COMMON_H */ #endif /* _ETHTOOL_COMMON_H */

View file

@ -2140,32 +2140,17 @@ static int ethtool_get_dump_data(struct net_device *dev,
static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr) static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr)
{ {
int err = 0;
struct ethtool_ts_info info; struct ethtool_ts_info info;
const struct ethtool_ops *ops = dev->ethtool_ops; int err;
struct phy_device *phydev = dev->phydev;
memset(&info, 0, sizeof(info));
info.cmd = ETHTOOL_GET_TS_INFO;
if (phy_has_tsinfo(phydev)) {
err = phy_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) if (err)
return err; return err;
if (copy_to_user(useraddr, &info, sizeof(info))) if (copy_to_user(useraddr, &info, sizeof(info)))
err = -EFAULT; return -EFAULT;
return err; return 0;
} }
static int __ethtool_get_module_info(struct net_device *dev, static int __ethtool_get_module_info(struct net_device *dev,

View file

@ -230,6 +230,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_COALESCE_GET] = &ethnl_coalesce_request_ops, [ETHTOOL_MSG_COALESCE_GET] = &ethnl_coalesce_request_ops,
[ETHTOOL_MSG_PAUSE_GET] = &ethnl_pause_request_ops, [ETHTOOL_MSG_PAUSE_GET] = &ethnl_pause_request_ops,
[ETHTOOL_MSG_EEE_GET] = &ethnl_eee_request_ops, [ETHTOOL_MSG_EEE_GET] = &ethnl_eee_request_ops,
[ETHTOOL_MSG_TSINFO_GET] = &ethnl_tsinfo_request_ops,
}; };
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@ -831,6 +832,13 @@ static const struct genl_ops ethtool_genl_ops[] = {
.flags = GENL_UNS_ADMIN_PERM, .flags = GENL_UNS_ADMIN_PERM,
.doit = ethnl_set_eee, .doit = ethnl_set_eee,
}, },
{
.cmd = ETHTOOL_MSG_TSINFO_GET,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
},
}; };
static const struct genl_multicast_group ethtool_nl_mcgrps[] = { static const struct genl_multicast_group ethtool_nl_mcgrps[] = {

View file

@ -344,6 +344,7 @@ extern const struct ethnl_request_ops ethnl_channels_request_ops;
extern const struct ethnl_request_ops ethnl_coalesce_request_ops; extern const struct ethnl_request_ops ethnl_coalesce_request_ops;
extern const struct ethnl_request_ops ethnl_pause_request_ops; extern const struct ethnl_request_ops ethnl_pause_request_ops;
extern const struct ethnl_request_ops ethnl_eee_request_ops; extern const struct ethnl_request_ops ethnl_eee_request_ops;
extern const struct ethnl_request_ops ethnl_tsinfo_request_ops;
int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);

143
net/ethtool/tsinfo.c Normal file
View file

@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/net_tstamp.h>
#include "netlink.h"
#include "common.h"
#include "bitset.h"
struct tsinfo_req_info {
struct ethnl_req_info base;
};
struct tsinfo_reply_data {
struct ethnl_reply_data base;
struct ethtool_ts_info ts_info;
};
#define TSINFO_REPDATA(__reply_base) \
container_of(__reply_base, struct tsinfo_reply_data, base)
static const struct nla_policy
tsinfo_get_policy[ETHTOOL_A_TSINFO_MAX + 1] = {
[ETHTOOL_A_TSINFO_UNSPEC] = { .type = NLA_REJECT },
[ETHTOOL_A_TSINFO_HEADER] = { .type = NLA_NESTED },
[ETHTOOL_A_TSINFO_TIMESTAMPING] = { .type = NLA_REJECT },
[ETHTOOL_A_TSINFO_TX_TYPES] = { .type = NLA_REJECT },
[ETHTOOL_A_TSINFO_RX_FILTERS] = { .type = NLA_REJECT },
[ETHTOOL_A_TSINFO_PHC_INDEX] = { .type = NLA_REJECT },
};
static int tsinfo_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
struct net_device *dev = reply_base->dev;
int ret;
ret = ethnl_ops_begin(dev);
if (ret < 0)
return ret;
ret = __ethtool_get_ts_info(dev, &data->ts_info);
ethnl_ops_complete(dev);
return ret;
}
static int tsinfo_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
const struct ethtool_ts_info *ts_info = &data->ts_info;
int len = 0;
int ret;
BUILD_BUG_ON(__SOF_TIMESTAMPING_CNT > 32);
BUILD_BUG_ON(__HWTSTAMP_TX_CNT > 32);
BUILD_BUG_ON(__HWTSTAMP_FILTER_CNT > 32);
if (ts_info->so_timestamping) {
ret = ethnl_bitset32_size(&ts_info->so_timestamping, NULL,
__SOF_TIMESTAMPING_CNT,
sof_timestamping_names, compact);
if (ret < 0)
return ret;
len += ret; /* _TSINFO_TIMESTAMPING */
}
if (ts_info->tx_types) {
ret = ethnl_bitset32_size(&ts_info->tx_types, NULL,
__HWTSTAMP_TX_CNT,
ts_tx_type_names, compact);
if (ret < 0)
return ret;
len += ret; /* _TSINFO_TX_TYPES */
}
if (ts_info->rx_filters) {
ret = ethnl_bitset32_size(&ts_info->rx_filters, NULL,
__HWTSTAMP_FILTER_CNT,
ts_rx_filter_names, compact);
if (ret < 0)
return ret;
len += ret; /* _TSINFO_RX_FILTERS */
}
if (ts_info->phc_index >= 0)
len += nla_total_size(sizeof(u32)); /* _TSINFO_PHC_INDEX */
return len;
}
static int tsinfo_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
const struct ethtool_ts_info *ts_info = &data->ts_info;
int ret;
if (ts_info->so_timestamping) {
ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_TIMESTAMPING,
&ts_info->so_timestamping, NULL,
__SOF_TIMESTAMPING_CNT,
sof_timestamping_names, compact);
if (ret < 0)
return ret;
}
if (ts_info->tx_types) {
ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_TX_TYPES,
&ts_info->tx_types, NULL,
__HWTSTAMP_TX_CNT,
ts_tx_type_names, compact);
if (ret < 0)
return ret;
}
if (ts_info->rx_filters) {
ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_RX_FILTERS,
&ts_info->rx_filters, NULL,
__HWTSTAMP_FILTER_CNT,
ts_rx_filter_names, compact);
if (ret < 0)
return ret;
}
if (ts_info->phc_index >= 0 &&
nla_put_u32(skb, ETHTOOL_A_TSINFO_PHC_INDEX, ts_info->phc_index))
return -EMSGSIZE;
return 0;
}
const struct ethnl_request_ops ethnl_tsinfo_request_ops = {
.request_cmd = ETHTOOL_MSG_TSINFO_GET,
.reply_cmd = ETHTOOL_MSG_TSINFO_GET_REPLY,
.hdr_attr = ETHTOOL_A_TSINFO_HEADER,
.max_attr = ETHTOOL_A_TSINFO_MAX,
.req_info_size = sizeof(struct tsinfo_req_info),
.reply_data_size = sizeof(struct tsinfo_reply_data),
.request_policy = tsinfo_get_policy,
.prepare_data = tsinfo_prepare_data,
.reply_size = tsinfo_reply_size,
.fill_reply = tsinfo_fill_reply,
};