Commit c13b5adb authored by David S. Miller's avatar David S. Miller

Merge branch 'ethtool-netlink-interface-part-4'

Michal Kubecek says:

====================
ethtool netlink interface, part 4

Implementation of more netlink request types:

  - coalescing (ethtool -c/-C, patches 2-4)
  - pause parameters (ethtool -a/-A, patches 5-7)
  - EEE settings (--show-eee / --set-eee, patches 8-10)
  - timestamping info (-T, patches 11-12)

Patch 1 is a fix for netdev reference leak similar to commit 2f599ec4
("ethtool: fix reference leak in some *_SET handlers") but fixing a code

Changes in v3
  - change "one-step-*" Tx type names to "onestep-*", (patch 11, suggested
    by Richard Cochran
  - use "TSINFO" rather than "TIMESTAMP" for timestamping information
    constants and adjust symbol names (patch 12, suggested by Richard
    Cochran)

Changes in v2:
  - fix compiler warning in net_hwtstamp_validate() (patch 11)
  - fix follow-up lines alignment (whitespace only, patches 3 and 8)
which is only in net-next tree at the moment.
====================
Acked-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 96376cad 5b071c59
......@@ -596,6 +596,9 @@ struct ethtool_pauseparam {
* @ETH_SS_LINK_MODES: link mode names
* @ETH_SS_MSG_CLASSES: debug message class names
* @ETH_SS_WOL_MODES: wake-on-lan modes
* @ETH_SS_SOF_TIMESTAMPING: SOF_TIMESTAMPING_* flags
* @ETH_SS_TS_TX_TYPES: timestamping Tx types
* @ETH_SS_TS_RX_FILTERS: timestamping Rx filters
*/
enum ethtool_stringset {
ETH_SS_TEST = 0,
......@@ -610,6 +613,9 @@ enum ethtool_stringset {
ETH_SS_LINK_MODES,
ETH_SS_MSG_CLASSES,
ETH_SS_WOL_MODES,
ETH_SS_SOF_TIMESTAMPING,
ETH_SS_TS_TX_TYPES,
ETH_SS_TS_RX_FILTERS,
/* add new constants above here */
ETH_SS_COUNT
......
......@@ -32,6 +32,13 @@ enum {
ETHTOOL_MSG_RINGS_SET,
ETHTOOL_MSG_CHANNELS_GET,
ETHTOOL_MSG_CHANNELS_SET,
ETHTOOL_MSG_COALESCE_GET,
ETHTOOL_MSG_COALESCE_SET,
ETHTOOL_MSG_PAUSE_GET,
ETHTOOL_MSG_PAUSE_SET,
ETHTOOL_MSG_EEE_GET,
ETHTOOL_MSG_EEE_SET,
ETHTOOL_MSG_TSINFO_GET,
/* add new constants above here */
__ETHTOOL_MSG_USER_CNT,
......@@ -60,6 +67,13 @@ enum {
ETHTOOL_MSG_RINGS_NTF,
ETHTOOL_MSG_CHANNELS_GET_REPLY,
ETHTOOL_MSG_CHANNELS_NTF,
ETHTOOL_MSG_COALESCE_GET_REPLY,
ETHTOOL_MSG_COALESCE_NTF,
ETHTOOL_MSG_PAUSE_GET_REPLY,
ETHTOOL_MSG_PAUSE_NTF,
ETHTOOL_MSG_EEE_GET_REPLY,
ETHTOOL_MSG_EEE_NTF,
ETHTOOL_MSG_TSINFO_GET_REPLY,
/* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT,
......@@ -310,6 +324,85 @@ enum {
ETHTOOL_A_CHANNELS_MAX = (__ETHTOOL_A_CHANNELS_CNT - 1)
};
/* COALESCE */
enum {
ETHTOOL_A_COALESCE_UNSPEC,
ETHTOOL_A_COALESCE_HEADER, /* nest - _A_HEADER_* */
ETHTOOL_A_COALESCE_RX_USECS, /* u32 */
ETHTOOL_A_COALESCE_RX_MAX_FRAMES, /* u32 */
ETHTOOL_A_COALESCE_RX_USECS_IRQ, /* u32 */
ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, /* u32 */
ETHTOOL_A_COALESCE_TX_USECS, /* u32 */
ETHTOOL_A_COALESCE_TX_MAX_FRAMES, /* u32 */
ETHTOOL_A_COALESCE_TX_USECS_IRQ, /* u32 */
ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, /* u32 */
ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, /* u32 */
ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, /* u8 */
ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, /* u8 */
ETHTOOL_A_COALESCE_PKT_RATE_LOW, /* u32 */
ETHTOOL_A_COALESCE_RX_USECS_LOW, /* u32 */
ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, /* u32 */
ETHTOOL_A_COALESCE_TX_USECS_LOW, /* u32 */
ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, /* u32 */
ETHTOOL_A_COALESCE_PKT_RATE_HIGH, /* u32 */
ETHTOOL_A_COALESCE_RX_USECS_HIGH, /* u32 */
ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, /* u32 */
ETHTOOL_A_COALESCE_TX_USECS_HIGH, /* u32 */
ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, /* u32 */
ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, /* u32 */
/* add new constants above here */
__ETHTOOL_A_COALESCE_CNT,
ETHTOOL_A_COALESCE_MAX = (__ETHTOOL_A_COALESCE_CNT - 1)
};
/* PAUSE */
enum {
ETHTOOL_A_PAUSE_UNSPEC,
ETHTOOL_A_PAUSE_HEADER, /* nest - _A_HEADER_* */
ETHTOOL_A_PAUSE_AUTONEG, /* u8 */
ETHTOOL_A_PAUSE_RX, /* u8 */
ETHTOOL_A_PAUSE_TX, /* u8 */
/* add new constants above here */
__ETHTOOL_A_PAUSE_CNT,
ETHTOOL_A_PAUSE_MAX = (__ETHTOOL_A_PAUSE_CNT - 1)
};
/* EEE */
enum {
ETHTOOL_A_EEE_UNSPEC,
ETHTOOL_A_EEE_HEADER, /* nest - _A_HEADER_* */
ETHTOOL_A_EEE_MODES_OURS, /* bitset */
ETHTOOL_A_EEE_MODES_PEER, /* bitset */
ETHTOOL_A_EEE_ACTIVE, /* u8 */
ETHTOOL_A_EEE_ENABLED, /* u8 */
ETHTOOL_A_EEE_TX_LPI_ENABLED, /* u8 */
ETHTOOL_A_EEE_TX_LPI_TIMER, /* u32 */
/* add new constants above here */
__ETHTOOL_A_EEE_CNT,
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 */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
......
......@@ -98,6 +98,9 @@ enum hwtstamp_tx_types {
* receive a time stamp via the socket error queue.
*/
HWTSTAMP_TX_ONESTEP_P2P,
/* add new constants above here */
__HWTSTAMP_TX_CNT
};
/* possible values for hwtstamp_config->rx_filter */
......@@ -140,6 +143,9 @@ enum hwtstamp_rx_filters {
/* NTP, UDP, all versions and packet modes */
HWTSTAMP_FILTER_NTP_ALL,
/* add new constants above here */
__HWTSTAMP_FILTER_CNT
};
/* SCM_TIMESTAMPING_PKTINFO control message */
......
......@@ -190,6 +190,9 @@ static int net_hwtstamp_validate(struct ifreq *ifr)
case HWTSTAMP_TX_ONESTEP_P2P:
tx_type_valid = 1;
break;
case __HWTSTAMP_TX_CNT:
/* not a real value */
break;
}
switch (rx_filter) {
......@@ -211,6 +214,9 @@ static int net_hwtstamp_validate(struct ifreq *ifr)
case HWTSTAMP_FILTER_NTP_ALL:
rx_filter_valid = 1;
break;
case __HWTSTAMP_FILTER_CNT:
/* not a real value */
break;
}
if (!tx_type_valid || !rx_filter_valid)
......
......@@ -6,4 +6,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
channels.o coalesce.o pause.o eee.o tsinfo.o
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/net_tstamp.h>
#include <linux/phy.h>
#include "common.h"
const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
......@@ -204,6 +207,53 @@ const char wol_mode_names[][ETH_GSTRING_LEN] = {
};
static_assert(ARRAY_SIZE(wol_mode_names) == WOL_MODE_COUNT);
const char sof_timestamping_names[][ETH_GSTRING_LEN] = {
[const_ilog2(SOF_TIMESTAMPING_TX_HARDWARE)] = "hardware-transmit",
[const_ilog2(SOF_TIMESTAMPING_TX_SOFTWARE)] = "software-transmit",
[const_ilog2(SOF_TIMESTAMPING_RX_HARDWARE)] = "hardware-receive",
[const_ilog2(SOF_TIMESTAMPING_RX_SOFTWARE)] = "software-receive",
[const_ilog2(SOF_TIMESTAMPING_SOFTWARE)] = "software-system-clock",
[const_ilog2(SOF_TIMESTAMPING_SYS_HARDWARE)] = "hardware-legacy-clock",
[const_ilog2(SOF_TIMESTAMPING_RAW_HARDWARE)] = "hardware-raw-clock",
[const_ilog2(SOF_TIMESTAMPING_OPT_ID)] = "option-id",
[const_ilog2(SOF_TIMESTAMPING_TX_SCHED)] = "sched-transmit",
[const_ilog2(SOF_TIMESTAMPING_TX_ACK)] = "ack-transmit",
[const_ilog2(SOF_TIMESTAMPING_OPT_CMSG)] = "option-cmsg",
[const_ilog2(SOF_TIMESTAMPING_OPT_TSONLY)] = "option-tsonly",
[const_ilog2(SOF_TIMESTAMPING_OPT_STATS)] = "option-stats",
[const_ilog2(SOF_TIMESTAMPING_OPT_PKTINFO)] = "option-pktinfo",
[const_ilog2(SOF_TIMESTAMPING_OPT_TX_SWHW)] = "option-tx-swhw",
};
static_assert(ARRAY_SIZE(sof_timestamping_names) == __SOF_TIMESTAMPING_CNT);
const char ts_tx_type_names[][ETH_GSTRING_LEN] = {
[HWTSTAMP_TX_OFF] = "off",
[HWTSTAMP_TX_ON] = "on",
[HWTSTAMP_TX_ONESTEP_SYNC] = "onestep-sync",
[HWTSTAMP_TX_ONESTEP_P2P] = "onestep-p2p",
};
static_assert(ARRAY_SIZE(ts_tx_type_names) == __HWTSTAMP_TX_CNT);
const char ts_rx_filter_names[][ETH_GSTRING_LEN] = {
[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",
};
static_assert(ARRAY_SIZE(ts_rx_filter_names) == __HWTSTAMP_FILTER_CNT);
/* return false if legacy contained non-0 deprecated fields
* maxtxpkt/maxrxpkt. rest of ksettings always updated
*/
......@@ -301,3 +351,23 @@ int ethtool_check_ops(const struct ethtool_ops *ops)
*/
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;
}
......@@ -12,6 +12,8 @@
#define ETHTOOL_LINK_MODE(speed, type, duplex) \
ETHTOOL_LINK_MODE_ ## speed ## base ## type ## _ ## duplex ## _BIT
#define __SOF_TIMESTAMPING_CNT (const_ilog2(SOF_TIMESTAMPING_LAST) + 1)
extern const char
netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN];
extern const char
......@@ -23,6 +25,9 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
extern const char link_mode_names[][ETH_GSTRING_LEN];
extern const char netif_msg_class_names[][ETH_GSTRING_LEN];
extern const char wol_mode_names[][ETH_GSTRING_LEN];
extern const char sof_timestamping_names[][ETH_GSTRING_LEN];
extern const char ts_tx_type_names[][ETH_GSTRING_LEN];
extern const char ts_rx_filter_names[][ETH_GSTRING_LEN];
int __ethtool_get_link(struct net_device *dev);
......@@ -30,5 +35,6 @@ bool convert_legacy_settings_to_link_ksettings(
struct ethtool_link_ksettings *link_ksettings,
const struct ethtool_cmd *legacy_settings);
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 */
// SPDX-License-Identifier: GPL-2.0-only
#include "netlink.h"
#include "common.h"
#include "bitset.h"
#define EEE_MODES_COUNT \
(sizeof_field(struct ethtool_eee, supported) * BITS_PER_BYTE)
struct eee_req_info {
struct ethnl_req_info base;
};
struct eee_reply_data {
struct ethnl_reply_data base;
struct ethtool_eee eee;
};
#define EEE_REPDATA(__reply_base) \
container_of(__reply_base, struct eee_reply_data, base)
static const struct nla_policy
eee_get_policy[ETHTOOL_A_EEE_MAX + 1] = {
[ETHTOOL_A_EEE_UNSPEC] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_HEADER] = { .type = NLA_NESTED },
[ETHTOOL_A_EEE_MODES_OURS] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_MODES_PEER] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_ACTIVE] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_ENABLED] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_TX_LPI_ENABLED] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_TX_LPI_TIMER] = { .type = NLA_REJECT },
};
static int eee_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
struct eee_reply_data *data = EEE_REPDATA(reply_base);
struct net_device *dev = reply_base->dev;
int ret;
if (!dev->ethtool_ops->get_eee)
return -EOPNOTSUPP;
ret = ethnl_ops_begin(dev);
if (ret < 0)
return ret;
ret = dev->ethtool_ops->get_eee(dev, &data->eee);
ethnl_ops_complete(dev);
return ret;
}
static int eee_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
const struct eee_reply_data *data = EEE_REPDATA(reply_base);
const struct ethtool_eee *eee = &data->eee;
int len = 0;
int ret;
BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE !=
EEE_MODES_COUNT);
BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE !=
EEE_MODES_COUNT);
/* MODES_OURS */
ret = ethnl_bitset32_size(&eee->advertised, &eee->supported,
EEE_MODES_COUNT, link_mode_names, compact);
if (ret < 0)
return ret;
len += ret;
/* MODES_PEERS */
ret = ethnl_bitset32_size(&eee->lp_advertised, NULL,
EEE_MODES_COUNT, link_mode_names, compact);
if (ret < 0)
return ret;
len += ret;
len += nla_total_size(sizeof(u8)) + /* _EEE_ACTIVE */
nla_total_size(sizeof(u8)) + /* _EEE_ENABLED */
nla_total_size(sizeof(u8)) + /* _EEE_TX_LPI_ENABLED */
nla_total_size(sizeof(u32)); /* _EEE_TX_LPI_TIMER */
return len;
}
static int eee_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
const struct eee_reply_data *data = EEE_REPDATA(reply_base);
const struct ethtool_eee *eee = &data->eee;
int ret;
ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS,
&eee->advertised, &eee->supported,
EEE_MODES_COUNT, link_mode_names, compact);
if (ret < 0)
return ret;
ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER,
&eee->lp_advertised, NULL, EEE_MODES_COUNT,
link_mode_names, compact);
if (ret < 0)
return ret;
if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) ||
nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) ||
nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED,
!!eee->tx_lpi_enabled) ||
nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer))
return -EMSGSIZE;
return 0;
}
const struct ethnl_request_ops ethnl_eee_request_ops = {
.request_cmd = ETHTOOL_MSG_EEE_GET,
.reply_cmd = ETHTOOL_MSG_EEE_GET_REPLY,
.hdr_attr = ETHTOOL_A_EEE_HEADER,
.max_attr = ETHTOOL_A_EEE_MAX,
.req_info_size = sizeof(struct eee_req_info),
.reply_data_size = sizeof(struct eee_reply_data),
.request_policy = eee_get_policy,
.prepare_data = eee_prepare_data,
.reply_size = eee_reply_size,
.fill_reply = eee_fill_reply,
};
/* EEE_SET */
static const struct nla_policy
eee_set_policy[ETHTOOL_A_EEE_MAX + 1] = {
[ETHTOOL_A_EEE_UNSPEC] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_HEADER] = { .type = NLA_NESTED },
[ETHTOOL_A_EEE_MODES_OURS] = { .type = NLA_NESTED },
[ETHTOOL_A_EEE_MODES_PEER] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_ACTIVE] = { .type = NLA_REJECT },
[ETHTOOL_A_EEE_ENABLED] = { .type = NLA_U8 },
[ETHTOOL_A_EEE_TX_LPI_ENABLED] = { .type = NLA_U8 },
[ETHTOOL_A_EEE_TX_LPI_TIMER] = { .type = NLA_U32 },
};
int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[ETHTOOL_A_EEE_MAX + 1];
struct ethtool_eee eee = {};
struct ethnl_req_info req_info = {};
const struct ethtool_ops *ops;
struct net_device *dev;
bool mod = false;
int ret;
ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, ETHTOOL_A_EEE_MAX,
eee_set_policy, info->extack);
if (ret < 0)
return ret;
ret = ethnl_parse_header_dev_get(&req_info,
tb[ETHTOOL_A_EEE_HEADER],
genl_info_net(info), info->extack,
true);
if (ret < 0)
return ret;
dev = req_info.dev;
ops = dev->ethtool_ops;
ret = -EOPNOTSUPP;
if (!ops->get_eee || !ops->set_eee)
goto out_dev;
rtnl_lock();
ret = ethnl_ops_begin(dev);
if (ret < 0)
goto out_rtnl;
ret = ops->get_eee(dev, &eee);
if (ret < 0)
goto out_ops;
ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT,
tb[ETHTOOL_A_EEE_MODES_OURS],
link_mode_names, info->extack, &mod);
if (ret < 0)
goto out_ops;
ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod);
ethnl_update_bool32(&eee.tx_lpi_enabled,
tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod);
ethnl_update_bool32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER],
&mod);
ret = 0;
if (!mod)
goto out_ops;
ret = dev->ethtool_ops->set_eee(dev, &eee);
if (ret < 0)
goto out_ops;
ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL);
out_ops:
ethnl_ops_complete(dev);
out_rtnl:
rtnl_unlock();
out_dev:
dev_put(dev);
return ret;
}
......@@ -1354,6 +1354,7 @@ static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)
static int ethtool_set_eee(struct net_device *dev, char __user *useraddr)
{
struct ethtool_eee edata;
int ret;
if (!dev->ethtool_ops->set_eee)
return -EOPNOTSUPP;
......@@ -1361,7 +1362,10 @@ static int ethtool_set_eee(struct net_device *dev, char __user *useraddr)
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
return dev->ethtool_ops->set_eee(dev, &edata);
ret = dev->ethtool_ops->set_eee(dev, &edata);
if (!ret)
ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL);
return ret;
}
static int ethtool_nway_reset(struct net_device *dev)
......@@ -1571,6 +1575,7 @@ static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
void __user *useraddr)
{
struct ethtool_coalesce coalesce;
int ret;
if (!dev->ethtool_ops->set_coalesce)
return -EOPNOTSUPP;
......@@ -1581,7 +1586,10 @@ static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
if (!ethtool_set_coalesce_supported(dev, &coalesce))
return -EOPNOTSUPP;
return dev->ethtool_ops->set_coalesce(dev, &coalesce);
ret = dev->ethtool_ops->set_coalesce(dev, &coalesce);
if (!ret)
ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL);
return ret;
}
static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
......@@ -1701,6 +1709,7 @@ static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
{
struct ethtool_pauseparam pauseparam;
int ret;
if (!dev->ethtool_ops->set_pauseparam)
return -EOPNOTSUPP;
......@@ -1708,7 +1717,10 @@ static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))
return -EFAULT;
return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
ret = dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
if (!ret)
ethtool_notify(dev, ETHTOOL_MSG_PAUSE_NTF, NULL);
return ret;
}
static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
......@@ -2128,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)
{
int err = 0;
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)) {
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;
}
int err;
err = __ethtool_get_ts_info(dev, &info);
if (err)
return err;
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,
......
......@@ -227,6 +227,10 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_PRIVFLAGS_GET] = &ethnl_privflags_request_ops,
[ETHTOOL_MSG_RINGS_GET] = &ethnl_rings_request_ops,
[ETHTOOL_MSG_CHANNELS_GET] = &ethnl_channels_request_ops,
[ETHTOOL_MSG_COALESCE_GET] = &ethnl_coalesce_request_ops,
[ETHTOOL_MSG_PAUSE_GET] = &ethnl_pause_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)
......@@ -543,6 +547,9 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
[ETHTOOL_MSG_PRIVFLAGS_NTF] = &ethnl_privflags_request_ops,
[ETHTOOL_MSG_RINGS_NTF] = &ethnl_rings_request_ops,
[ETHTOOL_MSG_CHANNELS_NTF] = &ethnl_channels_request_ops,
[ETHTOOL_MSG_COALESCE_NTF] = &ethnl_coalesce_request_ops,
[ETHTOOL_MSG_PAUSE_NTF] = &ethnl_pause_request_ops,
[ETHTOOL_MSG_EEE_NTF] = &ethnl_eee_request_ops,
};
/* default notification handler */
......@@ -632,6 +639,9 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
[ETHTOOL_MSG_PRIVFLAGS_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_RINGS_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_CHANNELS_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_COALESCE_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_PAUSE_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_EEE_NTF] = ethnl_default_notify,
};
void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
......@@ -786,6 +796,49 @@ static const struct genl_ops ethtool_genl_ops[] = {
.flags = GENL_UNS_ADMIN_PERM,
.doit = ethnl_set_channels,
},
{
.cmd = ETHTOOL_MSG_COALESCE_GET,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
},
{
.cmd = ETHTOOL_MSG_COALESCE_SET,
.flags = GENL_UNS_ADMIN_PERM,
.doit = ethnl_set_coalesce,
},
{
.cmd = ETHTOOL_MSG_PAUSE_GET,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
},
{
.cmd = ETHTOOL_MSG_PAUSE_SET,
.flags = GENL_UNS_ADMIN_PERM,
.doit = ethnl_set_pause,
},
{
.cmd = ETHTOOL_MSG_EEE_GET,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
},
{
.cmd = ETHTOOL_MSG_EEE_SET,
.flags = GENL_UNS_ADMIN_PERM,
.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[] = {
......
......@@ -341,6 +341,10 @@ extern const struct ethnl_request_ops ethnl_features_request_ops;
extern const struct ethnl_request_ops ethnl_privflags_request_ops;
extern const struct ethnl_request_ops ethnl_rings_request_ops;
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_pause_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_linkmodes(struct sk_buff *skb, struct genl_info *info);
......@@ -350,5 +354,8 @@ int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info);
#endif /* _NET_ETHTOOL_NETLINK_H */
// SPDX-License-Identifier: GPL-2.0-only
#include "netlink.h"
#include "common.h"
struct pause_req_info {
struct ethnl_req_info base;
};
struct pause_reply_data {
struct ethnl_reply_data base;
struct ethtool_pauseparam pauseparam;
};
#define PAUSE_REPDATA(__reply_base) \
container_of(__reply_base, struct pause_reply_data, base)
static const struct nla_policy
pause_get_policy[ETHTOOL_A_PAUSE_MAX + 1] = {
[ETHTOOL_A_PAUSE_UNSPEC] = { .type = NLA_REJECT },
[ETHTOOL_A_PAUSE_HEADER] = { .type = NLA_NESTED },
[ETHTOOL_A_PAUSE_AUTONEG] = { .type = NLA_REJECT },
[ETHTOOL_A_PAUSE_RX] = { .type = NLA_REJECT },
[ETHTOOL_A_PAUSE_TX] = { .type = NLA_REJECT },
};
static int pause_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
struct pause_reply_data *data = PAUSE_REPDATA(reply_base);
struct net_device *dev = reply_base->dev;
int ret;
if (!dev->ethtool_ops->get_pauseparam)
return -EOPNOTSUPP;
ret = ethnl_ops_begin(dev);
if (ret < 0)
return ret;
dev->ethtool_ops->get_pauseparam(dev, &data->pauseparam);
ethnl_ops_complete(dev);
return 0;
}
static int pause_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
return nla_total_size(sizeof(u8)) + /* _PAUSE_AUTONEG */
nla_total_size(sizeof(u8)) + /* _PAUSE_RX */
nla_total_size(sizeof(u8)); /* _PAUSE_TX */
}
static int pause_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct pause_reply_data *data = PAUSE_REPDATA(reply_base);
const struct ethtool_pauseparam *pauseparam = &data->pauseparam;
if (nla_put_u8(skb, ETHTOOL_A_PAUSE_AUTONEG, !!pauseparam->autoneg) ||
nla_put_u8(skb, ETHTOOL_A_PAUSE_RX, !!pauseparam->rx_pause) ||
nla_put_u8(skb, ETHTOOL_A_PAUSE_TX, !!pauseparam->tx_pause))
return -EMSGSIZE;
return 0;
}
const struct ethnl_request_ops ethnl_pause_request_ops = {
.request_cmd = ETHTOOL_MSG_PAUSE_GET,
.reply_cmd = ETHTOOL_MSG_PAUSE_GET_REPLY,
.hdr_attr = ETHTOOL_A_PAUSE_HEADER,
.max_attr = ETHTOOL_A_PAUSE_MAX,
.req_info_size = sizeof(struct pause_req_info),
.reply_data_size = sizeof(struct pause_reply_data),
.request_policy = pause_get_policy,
.prepare_data = pause_prepare_data,
.reply_size = pause_reply_size,
.fill_reply = pause_fill_reply,
};
/* PAUSE_SET */
static const struct nla_policy
pause_set_policy[ETHTOOL_A_PAUSE_MAX + 1] = {
[ETHTOOL_A_PAUSE_UNSPEC] = { .type = NLA_REJECT },
[ETHTOOL_A_PAUSE_HEADER] = { .type = NLA_NESTED },
[ETHTOOL_A_PAUSE_AUTONEG] = { .type = NLA_U8 },
[ETHTOOL_A_PAUSE_RX] = { .type = NLA_U8 },
[ETHTOOL_A_PAUSE_TX] = { .type = NLA_U8 },
};
int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[ETHTOOL_A_PAUSE_MAX + 1];
struct ethtool_pauseparam params = {};
struct ethnl_req_info req_info = {};
const struct ethtool_ops *ops;
struct net_device *dev;
bool mod = false;
int ret;
ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, ETHTOOL_A_PAUSE_MAX,
pause_set_policy, info->extack);
if (ret < 0)
return ret;
ret = ethnl_parse_header_dev_get(&req_info,
tb[ETHTOOL_A_PAUSE_HEADER],
genl_info_net(info), info->extack,
true);
if (ret < 0)
return ret;
dev = req_info.dev;
ops = dev->ethtool_ops;
ret = -EOPNOTSUPP;
if (!ops->get_pauseparam || !ops->set_pauseparam)
goto out_dev;
rtnl_lock();
ret = ethnl_ops_begin(dev);
if (ret < 0)
goto out_rtnl;
ops->get_pauseparam(dev, &params);
ethnl_update_bool32(&params.autoneg, tb[ETHTOOL_A_PAUSE_AUTONEG], &mod);
ethnl_update_bool32(&params.rx_pause, tb[ETHTOOL_A_PAUSE_RX], &mod);
ethnl_update_bool32(&params.tx_pause, tb[ETHTOOL_A_PAUSE_TX], &mod);
ret = 0;
if (!mod)
goto out_ops;
ret = dev->ethtool_ops->set_pauseparam(dev, &params);
if (ret < 0)
goto out_ops;
ethtool_notify(dev, ETHTOOL_MSG_PAUSE_NTF, NULL);
out_ops:
ethnl_ops_complete(dev);
out_rtnl:
rtnl_unlock();
out_dev:
dev_put(dev);
return ret;
}
......@@ -175,9 +175,10 @@ int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info)
return ret;
dev = req_info.dev;
ops = dev->ethtool_ops;
ret = -EOPNOTSUPP;
if (!ops->get_priv_flags || !ops->set_priv_flags ||
!ops->get_sset_count || !ops->get_strings)
return -EOPNOTSUPP;
goto out_dev;
rtnl_lock();
ret = ethnl_ops_begin(dev);
......@@ -204,6 +205,7 @@ int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info)
ethnl_ops_complete(dev);
out_rtnl:
rtnl_unlock();
out_dev:
dev_put(dev);
return ret;
}
......@@ -60,6 +60,21 @@ static const struct strset_info info_template[] = {
.count = WOL_MODE_COUNT,
.strings = wol_mode_names,
},
[ETH_SS_SOF_TIMESTAMPING] = {
.per_dev = false,
.count = __SOF_TIMESTAMPING_CNT,
.strings = sof_timestamping_names,
},
[ETH_SS_TS_TX_TYPES] = {
.per_dev = false,
.count = __HWTSTAMP_TX_CNT,
.strings = ts_tx_type_names,
},
[ETH_SS_TS_RX_FILTERS] = {
.per_dev = false,
.count = __HWTSTAMP_FILTER_CNT,
.strings = ts_rx_filter_names,
},
};
struct strset_req_info {
......
// 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,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment