[RFC PATCH 8/9] ethtool: implement GET_PARAMS message

From: Michal Kubecek
Date: Mon Dec 11 2017 - 08:54:44 EST


Requests the information provide by ETHTOOL_GCOALESCE, ETHTOOL_GRINGPARAM,
ETHTOOL_GPAUSEPARAM, ETHTOOL_GCHANNELS, ETHTOOL_GEEE and ETHTOOL_GFECPARAM.
Flags in info_mask allow selecting only some (or on) of these categories.

Signed-off-by: Michal Kubecek <mkubecek@xxxxxxx>
---
Documentation/networking/ethtool-netlink.txt | 98 ++++++-
include/uapi/linux/ethtool_netlink.h | 118 ++++++++
net/core/ethtool_netlink.c | 411 +++++++++++++++++++++++++++
3 files changed, 621 insertions(+), 6 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 187fab33f721..45ed1801ab50 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -111,6 +111,8 @@ List of message types
ETHTOOL_CMD_SET_DRVINFO response only
ETHTOOL_CMD_GET_SETTINGS
ETHTOOL_CMD_SET_SETTINGS
+ ETHTOOL_CMD_GET_PARAMS
+ ETHTOOL_CMD_SET_PARAMS response only (for now)

All constants use ETHTOOL_CMD_ prefix followed by "GET", "SET" or "ACT" to
indicate the type.
@@ -231,6 +233,90 @@ is supposed to allow requesting changes without knowing what exactly kernel
supports.


+GET_PARAMS
+----------
+
+GET_PARAMS request retrieves information provided by legacy comands
+ETHTOOL_GCOALESCE (coalescing setting), ETHTOOL_GRINGPARAM (ring parameters),
+ETHTOOL_GPAUSEPARAM (pause parameters), ETHTOOL_GCHANNELS (channel settings),
+ETHTOOL_GEEE (EEE settings) and ETHTOOL_GFECPARAM (FEC parameters). For each
+of these, there is a bit in header info_mask so that only one type of
+information can be requested.
+
+Header flags:
+ ETH_PARAMS_RF_COMPACT_BITSETS bitset form in response (1 is compact)
+
+Header info_mask bits:
+ ETH_PARAMS_IM_COALESCE coalescing settings
+ ETH_PARAMS_IM_RING ring parameters
+ ETH_PARAMS_IM_PAUSE pause parameters
+ ETH_PARAMS_IM_CHANNELS channel settings
+ ETH_PARAMS_IM_EEE EEE settings
+ ETH_PARAMS_IM_FEC FEC parameters
+
+On top level, there is one attribute for each of the information categories,
+the information is nested in it.
+
+ ETHA_PARAMS_COALESCE (nest) coalescing
+ ETHA_COALESCE_RX_USECS (u32)
+ ETHA_COALESCE_RX_MAXFRM (u32)
+ ETHA_COALESCE_RX_USECS_IRQ (u32)
+ ETHA_COALESCE_RX_MAXFRM_IRQ (u32)
+ ETHA_COALESCE_RX_USECS_LOW (u32)
+ ETHA_COALESCE_RX_MAXFRM_LOW (u32)
+ ETHA_COALESCE_RX_USECS_HIGH (u32)
+ ETHA_COALESCE_RX_MAXFRM_HIGH (u32)
+ ETHA_COALESCE_TX_USECS (u32)
+ ETHA_COALESCE_TX_MAXFRM (u32)
+ ETHA_COALESCE_TX_USECS_IRQ (u32)
+ ETHA_COALESCE_TX_MAXFRM_IRQ (u32)
+ ETHA_COALESCE_TX_USECS_LOW (u32)
+ ETHA_COALESCE_TX_MAXFRM_LOW (u32)
+ ETHA_COALESCE_TX_USECS_HIGH (u32)
+ ETHA_COALESCE_TX_MAXFRM_HIGH (u32)
+ ETHA_COALESCE_PKT_RATE_LOW (u32)
+ ETHA_COALESCE_PKT_RATE_HIGH (u32)
+ ETHA_COALESCE_RX_USE_ADAPTIVE (bool)
+ ETHA_COALESCE_TX_USE_ADAPTIVE (bool)
+ ETHA_COALESCE_RATE_SAMPLE_INTERVAL (u32)
+ ETHA_COALESCE_STATS_BLOCK_USECS (u32)
+ ETHA_PARAMS_RING (nest) ring parameters
+ ETHA_RING_RX_MAX_PENDING (u32)
+ ETHA_RING_RX_MINI_MAX_PENDING (u32)
+ ETHA_RING_RX_JUMBO_MAX_PENDING (u32)
+ ETHA_RING_TX_MAX_PENDING (u32)
+ ETHA_RING_RX_PENDING (u32)
+ ETHA_RING_RX_MINI_PENDING (u32)
+ ETHA_RING_RX_JUMBO_PENDING (u32)
+ ETHA_RING_TX_PENDING (u32)
+ ETHA_PARAMS_PAUSE (nest) pause parameters
+ ETHA_PAUSE_AUTONEG (bool)
+ ETHA_PAUSE_RX (bool)
+ ETHA_PAUSE_TX (bool)
+ ETHA_PARAMS_CHANNELS (nest) channel settings
+ ETHA_CHANNELS_MAX_RX (u32)
+ ETHA_CHANNELS_MAX_TX (u32)
+ ETHA_CHANNELS_MAX_OTHER (u32)
+ ETHA_CHANNELS_MAX_COMBINED (u32)
+ ETHA_CHANNELS_RX_COUNT (u32)
+ ETHA_CHANNELS_TX_COUNT (u32)
+ ETHA_CHANNELS_OTHER_COUNT (u32)
+ ETHA_CHANNELS_COMBINED_COUNT (u32)
+ ETHA_PARAMS_EEE (nest) EEE settings
+ ETHA_EEE_LINK_MODES (bitset)
+ - modes for which EEE is advertised (value) or supported (mask)
+ ETHA_EEE_PEER_MODES (bitset)
+ - modes for which link partner advertises EEE
+ ETHA_EEE_ACTIVE (bool)
+ ETHA_EEE_ENABLED (bool)
+ ETHA_EEE_TX_LPI_ENABLED (bool)
+ ETHA_EEE_TX_LPI_TIMER (u32)
+ ETHA_PARAMS_FEC (nest) FEC parameters
+ ETHA_FEC_MODES (bitfield32)
+ - active (value) and configured (selector) FEC encodings
+
+
+
Request translation
-------------------

@@ -252,11 +338,11 @@ ETHTOOL_NWAY_RST n/a
ETHTOOL_GLINK ETHTOOL_CMD_GET_SETTINGS
ETHTOOL_GEEPROM n/a
ETHTOOL_SEEPROM n/a
-ETHTOOL_GCOALESCE n/a
+ETHTOOL_GCOALESCE ETHTOOL_CMD_GET_PARAMS
ETHTOOL_SCOALESCE n/a
-ETHTOOL_GRINGPARAM n/a
+ETHTOOL_GRINGPARAM ETHTOOL_CMD_GET_PARAMS
ETHTOOL_SRINGPARAM n/a
-ETHTOOL_GPAUSEPARAM n/a
+ETHTOOL_GPAUSEPARAM ETHTOOL_CMD_GET_PARAMS
ETHTOOL_SPAUSEPARAM n/a
ETHTOOL_GRXCSUM n/a
ETHTOOL_SRXCSUM n/a
@@ -298,7 +384,7 @@ ETHTOOL_GRXFHINDIR n/a
ETHTOOL_SRXFHINDIR n/a
ETHTOOL_GFEATURES n/a
ETHTOOL_SFEATURES n/a
-ETHTOOL_GCHANNELS n/a
+ETHTOOL_GCHANNELS ETHTOOL_CMD_GET_PARAMS
ETHTOOL_SCHANNELS n/a
ETHTOOL_SET_DUMP n/a
ETHTOOL_GET_DUMP_FLAG n/a
@@ -306,7 +392,7 @@ ETHTOOL_GET_DUMP_DATA n/a
ETHTOOL_GET_TS_INFO n/a
ETHTOOL_GMODULEINFO n/a
ETHTOOL_GMODULEEEPROM n/a
-ETHTOOL_GEEE n/a
+ETHTOOL_GEEE ETHTOOL_CMD_GET_PARAMS
ETHTOOL_SEEE n/a
ETHTOOL_GRSSH n/a
ETHTOOL_SRSSH n/a
@@ -318,6 +404,6 @@ ETHTOOL_GLINKSETTINGS ETHTOOL_CMD_GET_SETTINGS
ETHTOOL_SLINKSETTINGS ETHTOOL_CMD_SET_SETTINGS
ETHTOOL_PHY_GTUNABLE n/a
ETHTOOL_PHY_STUNABLE n/a
-ETHTOOL_GFECPARAM n/a
+ETHTOOL_GFECPARAM ETHTOOL_CMD_GET_PARAMS
ETHTOOL_SFECPARAM n/a

diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 9520d13fc9ab..8621e0a9d481 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -25,6 +25,8 @@ enum {
ETHTOOL_CMD_SET_DRVINFO, /* only for reply */
ETHTOOL_CMD_GET_SETTINGS,
ETHTOOL_CMD_SET_SETTINGS,
+ ETHTOOL_CMD_GET_PARAMS,
+ ETHTOOL_CMD_SET_PARAMS,

__ETHTOOL_CMD_MAX,
ETHTOOL_CMD_MAX = (__ETHTOOL_CMD_MAX - 1),
@@ -114,6 +116,122 @@ enum {

#define ETH_SETTINGS_IM_DEFAULT 0x1f

+/* GET_PARAMS / SET_PARAMS */
+
+enum {
+ ETHA_PARAMS_UNSPEC,
+ ETHA_PARAMS_COALESCE, /* nest - ETHA_COALESCE_* */
+ ETHA_PARAMS_RING, /* nest - ETHA_RING_* */
+ ETHA_PARAMS_PAUSE, /* nest - ETHA_PAUSE_* */
+ ETHA_PARAMS_CHANNELS, /* nest - ETHA_CHANNELS_* */
+ ETHA_PARAMS_EEE, /* nest - ETHA_EEE_* */
+ ETHA_PARAMS_FEC, /* nest - ETHA_FEC_* */
+
+ __ETHA_PARAMS_MAX,
+ ETHA_PARAMS_MAX = (__ETHA_PARAMS_MAX - 1),
+};
+
+#define ETH_PARAMS_RF_COMPACT_BITSETS 0x1
+
+#define ETH_PARAMS_IM_COALESCE 0x01
+#define ETH_PARAMS_IM_RING 0x02
+#define ETH_PARAMS_IM_PAUSE 0x04
+#define ETH_PARAMS_IM_CHANNELS 0x08
+#define ETH_PARAMS_IM_EEE 0x10
+#define ETH_PARAMS_IM_FEC 0x20
+
+#define ETH_PARAMS_IM_DEFAULT 0x3f
+
+enum {
+ ETHA_COALESCE_UNSPEC,
+ ETHA_COALESCE_RX_USECS, /* u32 */
+ ETHA_COALESCE_RX_MAXFRM, /* u32 */
+ ETHA_COALESCE_RX_USECS_IRQ, /* u32 */
+ ETHA_COALESCE_RX_MAXFRM_IRQ, /* u32 */
+ ETHA_COALESCE_RX_USECS_LOW, /* u32 */
+ ETHA_COALESCE_RX_MAXFRM_LOW, /* u32 */
+ ETHA_COALESCE_RX_USECS_HIGH, /* u32 */
+ ETHA_COALESCE_RX_MAXFRM_HIGH, /* u32 */
+ ETHA_COALESCE_TX_USECS, /* u32 */
+ ETHA_COALESCE_TX_MAXFRM, /* u32 */
+ ETHA_COALESCE_TX_USECS_IRQ, /* u32 */
+ ETHA_COALESCE_TX_MAXFRM_IRQ, /* u32 */
+ ETHA_COALESCE_TX_USECS_LOW, /* u32 */
+ ETHA_COALESCE_TX_MAXFRM_LOW, /* u32 */
+ ETHA_COALESCE_TX_USECS_HIGH, /* u32 */
+ ETHA_COALESCE_TX_MAXFRM_HIGH, /* u32 */
+ ETHA_COALESCE_PKT_RATE_LOW, /* u32 */
+ ETHA_COALESCE_PKT_RATE_HIGH, /* u32 */
+ ETHA_COALESCE_RX_USE_ADAPTIVE, /* u8 */
+ ETHA_COALESCE_TX_USE_ADAPTIVE, /* u8 */
+ ETHA_COALESCE_RATE_SAMPLE_INTERVAL, /* u32 */
+ ETHA_COALESCE_STATS_BLOCK_USECS, /* u32 */
+
+ __ETHA_COALESCE_MAX,
+ ETHA_COALESCE_MAX = (__ETHA_COALESCE_MAX - 1),
+};
+
+enum {
+ ETHA_RING_UNSPEC,
+ ETHA_RING_RX_MAX_PENDING, /* u32 */
+ ETHA_RING_RX_MINI_MAX_PENDING, /* u32 */
+ ETHA_RING_RX_JUMBO_MAX_PENDING, /* u32 */
+ ETHA_RING_TX_MAX_PENDING, /* u32 */
+ ETHA_RING_RX_PENDING, /* u32 */
+ ETHA_RING_RX_MINI_PENDING, /* u32 */
+ ETHA_RING_RX_JUMBO_PENDING, /* u32 */
+ ETHA_RING_TX_PENDING, /* u32 */
+
+ __ETHA_RING_MAX,
+ ETHA_RING_MAX = (__ETHA_RING_MAX - 1),
+};
+
+enum {
+ ETHA_PAUSE_UNSPEC,
+ ETHA_PAUSE_AUTONEG, /* u8 */
+ ETHA_PAUSE_RX, /* u8 */
+ ETHA_PAUSE_TX, /* u8 */
+
+ __ETHA_PAUSE_MAX,
+ ETHA_PAUSE_MAX = (__ETHA_PAUSE_MAX - 1),
+};
+
+enum {
+ ETHA_CHANNELS_UNSPEC,
+ ETHA_CHANNELS_MAX_RX, /* u32 */
+ ETHA_CHANNELS_MAX_TX, /* u32 */
+ ETHA_CHANNELS_MAX_OTHER, /* u32 */
+ ETHA_CHANNELS_MAX_COMBINED, /* u32 */
+ ETHA_CHANNELS_RX_COUNT, /* u32 */
+ ETHA_CHANNELS_TX_COUNT, /* u32 */
+ ETHA_CHANNELS_OTHER_COUNT, /* u32 */
+ ETHA_CHANNELS_COMBINED_COUNT, /* u32 */
+
+ __ETHA_CHANNELS_MAX,
+ ETHA_CHANNELS_MAX = (__ETHA_CHANNELS_MAX - 1),
+};
+
+enum {
+ ETHA_EEE_UNSPEC,
+ ETHA_EEE_LINK_MODES, /* bitset */
+ ETHA_EEE_PEER_MODES, /* bitset */
+ ETHA_EEE_ACTIVE, /* u8 */
+ ETHA_EEE_ENABLED, /* u8 */
+ ETHA_EEE_TX_LPI_ENABLED, /* u8 */
+ ETHA_EEE_TX_LPI_TIMER, /* u32 */
+
+ __ETHA_EEE_MAX,
+ ETHA_EEE_MAX = (__ETHA_EEE_MAX - 1),
+};
+
+enum {
+ ETHA_FEC_UNSPEC,
+ ETHA_FEC_MODES, /* bitfield32 */
+
+ __ETHA_FEC_MAX,
+ ETHA_FEC_MAX = (__ETHA_FEC_MAX - 1),
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
index 4b14a02be12a..3fb49427f211 100644
--- a/net/core/ethtool_netlink.c
+++ b/net/core/ethtool_netlink.c
@@ -1438,6 +1438,413 @@ static int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
return ret;
}

+/* GET_PARAMS */
+
+static int ethnl_get_coalesce(struct net_device *dev,
+ struct ethtool_coalesce *data)
+{
+ if (!dev->ethtool_ops->get_coalesce)
+ return -EOPNOTSUPP;
+ return dev->ethtool_ops->get_coalesce(dev, data);
+}
+
+static int ethnl_get_ring(struct net_device *dev,
+ struct ethtool_ringparam *data)
+{
+ if (!dev->ethtool_ops->get_ringparam)
+ return -EOPNOTSUPP;
+ dev->ethtool_ops->get_ringparam(dev, data);
+ return 0;
+}
+
+static int ethnl_get_pause(struct net_device *dev,
+ struct ethtool_pauseparam *data)
+{
+ if (!dev->ethtool_ops->get_pauseparam)
+ return -EOPNOTSUPP;
+ dev->ethtool_ops->get_pauseparam(dev, data);
+ return 0;
+}
+
+static int ethnl_get_channels(struct net_device *dev,
+ struct ethtool_channels *data)
+{
+ if (!dev->ethtool_ops->get_channels)
+ return -EOPNOTSUPP;
+ dev->ethtool_ops->get_channels(dev, data);
+ return 0;
+}
+
+static int ethnl_get_eee(struct net_device *dev, struct ethtool_eee *data)
+{
+ if (!dev->ethtool_ops->get_eee)
+ return -EOPNOTSUPP;
+ return dev->ethtool_ops->get_eee(dev, data);
+}
+
+static int ethnl_get_fec(struct net_device *dev, struct ethtool_fecparam *data)
+{
+ if (!dev->ethtool_ops->get_fecparam)
+ return -EOPNOTSUPP;
+ return dev->ethtool_ops->get_fecparam(dev, data);
+}
+
+static int ethnl_params_size(struct ethtool_eee *eee, u16 flags, u16 mask)
+{
+ int len = 0;
+
+ if (mask & ETH_PARAMS_IM_COALESCE)
+ len += nla_total_size(20 * nla_total_size(sizeof(u32)) +
+ 2 * nla_total_size(sizeof(u8)));
+ if (mask & ETH_PARAMS_IM_RING)
+ len += nla_total_size(8 * nla_total_size(sizeof(u32)));
+ if (mask & ETH_PARAMS_IM_PAUSE)
+ len += nla_total_size(3 * nla_total_size(sizeof(u8)));
+ if (mask & ETH_PARAMS_IM_CHANNELS)
+ len += nla_total_size(8 * nla_total_size(sizeof(u32)));
+ if (mask & ETH_PARAMS_IM_EEE) {
+ int nlen = 0;
+ int ret;
+
+ /* link_modes */
+ ret = ethnl_bitset_size(flags & ETH_PARAMS_RF_COMPACT_BITSETS,
+ sizeof(u32) * 8, &eee->advertised,
+ &eee->supported, link_mode_names);
+ if (ret < 0)
+ return ret;
+ nlen += ret;
+ /* peer_modes */
+ ret = ethnl_bitset_size(flags & ETH_PARAMS_RF_COMPACT_BITSETS,
+ sizeof(u32) * 8, &eee->lp_advertised,
+ &eee->lp_advertised, link_mode_names);
+ if (ret < 0)
+ return ret;
+ nlen += ret;
+ /* active, enabled, tx_lpi_enabled */
+ nlen += 3 * nla_total_size(sizeof(u8));
+ /* tx_lpi_timer */
+ nlen += nla_total_size(sizeof(u32));
+ /* nest */
+ len += nla_total_size(nlen);
+ }
+ if (mask & ETH_PARAMS_IM_FEC) {
+ int nlen = nla_total_size(sizeof(struct nla_bitfield32));
+
+ len += nla_total_size(nlen);
+ }
+
+ return len;
+}
+
+static int fill_coalesce(struct sk_buff *skb, struct ethtool_coalesce *data)
+{
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_COALESCE);
+
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_u32(skb, ETHA_COALESCE_RX_USECS,
+ data->rx_coalesce_usecs) ||
+ nla_put_u32(skb, ETHA_COALESCE_RX_MAXFRM,
+ data->rx_max_coalesced_frames) ||
+ nla_put_u32(skb, ETHA_COALESCE_RX_USECS_IRQ,
+ data->rx_coalesce_usecs_irq) ||
+ nla_put_u32(skb, ETHA_COALESCE_RX_MAXFRM_IRQ,
+ data->rx_max_coalesced_frames_irq) ||
+ nla_put_u32(skb, ETHA_COALESCE_RX_USECS_LOW,
+ data->rx_coalesce_usecs_low) ||
+ nla_put_u32(skb, ETHA_COALESCE_RX_MAXFRM_LOW,
+ data->rx_max_coalesced_frames_low) ||
+ nla_put_u32(skb, ETHA_COALESCE_RX_USECS_HIGH,
+ data->rx_coalesce_usecs_high) ||
+ nla_put_u32(skb, ETHA_COALESCE_RX_MAXFRM_HIGH,
+ data->rx_max_coalesced_frames_high) ||
+ nla_put_u32(skb, ETHA_COALESCE_TX_USECS,
+ data->tx_coalesce_usecs) ||
+ nla_put_u32(skb, ETHA_COALESCE_TX_MAXFRM,
+ data->tx_max_coalesced_frames) ||
+ nla_put_u32(skb, ETHA_COALESCE_TX_USECS_IRQ,
+ data->tx_coalesce_usecs_irq) ||
+ nla_put_u32(skb, ETHA_COALESCE_TX_MAXFRM_IRQ,
+ data->tx_max_coalesced_frames_irq) ||
+ nla_put_u32(skb, ETHA_COALESCE_TX_USECS_LOW,
+ data->tx_coalesce_usecs_low) ||
+ nla_put_u32(skb, ETHA_COALESCE_TX_MAXFRM_LOW,
+ data->tx_max_coalesced_frames_low) ||
+ nla_put_u32(skb, ETHA_COALESCE_TX_USECS_HIGH,
+ data->tx_coalesce_usecs_high) ||
+ nla_put_u32(skb, ETHA_COALESCE_TX_MAXFRM_HIGH,
+ data->tx_max_coalesced_frames_high) ||
+ nla_put_u32(skb, ETHA_COALESCE_PKT_RATE_LOW,
+ data->pkt_rate_low) ||
+ nla_put_u32(skb, ETHA_COALESCE_PKT_RATE_HIGH,
+ data->pkt_rate_high) ||
+ nla_put_u8(skb, ETHA_COALESCE_RX_USE_ADAPTIVE,
+ !!data->use_adaptive_rx_coalesce) ||
+ nla_put_u8(skb, ETHA_COALESCE_TX_USE_ADAPTIVE,
+ !!data->use_adaptive_tx_coalesce) ||
+ nla_put_u32(skb, ETHA_COALESCE_RATE_SAMPLE_INTERVAL,
+ data->rate_sample_interval) ||
+ nla_put_u32(skb, ETHA_COALESCE_STATS_BLOCK_USECS,
+ data->stats_block_coalesce_usecs)) {
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+}
+
+static int fill_ring(struct sk_buff *skb, struct ethtool_ringparam *data)
+{
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_RING);
+
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_u32(skb, ETHA_RING_RX_MAX_PENDING,
+ data->rx_max_pending) ||
+ nla_put_u32(skb, ETHA_RING_RX_MINI_MAX_PENDING,
+ data->rx_mini_max_pending) ||
+ nla_put_u32(skb, ETHA_RING_RX_JUMBO_MAX_PENDING,
+ data->rx_jumbo_max_pending) ||
+ nla_put_u32(skb, ETHA_RING_TX_MAX_PENDING,
+ data->tx_max_pending) ||
+ nla_put_u32(skb, ETHA_RING_RX_PENDING,
+ data->rx_pending) ||
+ nla_put_u32(skb, ETHA_RING_RX_MINI_PENDING,
+ data->rx_mini_pending) ||
+ nla_put_u32(skb, ETHA_RING_RX_JUMBO_PENDING,
+ data->rx_jumbo_pending) ||
+ nla_put_u32(skb, ETHA_RING_TX_PENDING,
+ data->tx_pending)) {
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+}
+
+static int fill_pause(struct sk_buff *skb, struct ethtool_pauseparam *data)
+{
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_PAUSE);
+
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_u8(skb, ETHA_PAUSE_AUTONEG, !!data->autoneg) ||
+ nla_put_u8(skb, ETHA_PAUSE_RX, !!data->rx_pause) ||
+ nla_put_u8(skb, ETHA_PAUSE_TX, !!data->tx_pause)) {
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+}
+
+static int fill_channels(struct sk_buff *skb, struct ethtool_channels *data)
+{
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_CHANNELS);
+
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_u32(skb, ETHA_CHANNELS_MAX_RX, data->max_rx) ||
+ nla_put_u32(skb, ETHA_CHANNELS_MAX_TX, data->max_tx) ||
+ nla_put_u32(skb, ETHA_CHANNELS_MAX_OTHER, data->max_other) ||
+ nla_put_u32(skb, ETHA_CHANNELS_MAX_COMBINED, data->max_combined) ||
+ nla_put_u32(skb, ETHA_CHANNELS_RX_COUNT, data->rx_count) ||
+ nla_put_u32(skb, ETHA_CHANNELS_TX_COUNT, data->tx_count) ||
+ nla_put_u32(skb, ETHA_CHANNELS_OTHER_COUNT, data->other_count) ||
+ nla_put_u32(skb, ETHA_CHANNELS_COMBINED_COUNT,
+ data->combined_count)) {
+ return -EMSGSIZE;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+}
+
+static int fill_eee(struct sk_buff *skb, struct ethtool_eee *data, u16 flags)
+{
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_EEE);
+ int ret;
+
+ if (!nest)
+ return -EMSGSIZE;
+ ret = ethnl_put_bitset(skb, ETHA_EEE_LINK_MODES,
+ flags & ETH_PARAMS_RF_COMPACT_BITSETS,
+ sizeof(data->advertised) * 8, &data->advertised,
+ &data->supported, link_mode_names);
+ if (ret < 0)
+ goto err;
+ ret = ethnl_put_bitset(skb, ETHA_EEE_PEER_MODES,
+ flags & ETH_PARAMS_RF_COMPACT_BITSETS,
+ sizeof(data->advertised) * 8,
+ &data->lp_advertised, &data->lp_advertised,
+ link_mode_names);
+ if (ret < 0)
+ goto err;
+
+ if (nla_put_u8(skb, ETHA_EEE_ACTIVE, !!data->eee_active) ||
+ nla_put_u8(skb, ETHA_EEE_ENABLED, !!data->eee_enabled) ||
+ nla_put_u8(skb, ETHA_EEE_TX_LPI_ENABLED, !!data->tx_lpi_enabled) ||
+ nla_put_u32(skb, ETHA_EEE_TX_LPI_TIMER, data->tx_lpi_timer)) {
+ ret = -EMSGSIZE;
+ goto err;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+err:
+ nla_nest_cancel(skb, nest);
+ return ret;
+}
+
+static int fill_fec(struct sk_buff *skb, struct ethtool_fecparam *data)
+{
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_FEC);
+
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_bitfield32(skb, ETHA_FEC_MODES, data->active_fec,
+ data->fec)) {
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+}
+
+static int ethnl_get_params(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ethtool_coalesce coalesce = {};
+ struct ethtool_channels channels = {};
+ struct ethtool_pauseparam pause = {};
+ struct ethtool_ringparam ring = {};
+ struct ethtool_fecparam fec = {};
+ struct ethtool_eee eee = {};
+ unsigned int reply_len = 0;
+ struct ethnlmsghdr *ehdr;
+ struct net_device *dev;
+ struct sk_buff *rskb;
+ u16 req_flags;
+ u16 req_mask;
+ int ret = 0;
+
+ ehdr = info->userhdr;
+ if (!ehdr->info_mask)
+ ehdr->info_mask = ETH_PARAMS_IM_DEFAULT;
+ req_mask = ehdr->info_mask;
+ req_flags = ehdr->flags;
+
+ dev = ethnl_dev_get(info);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ if (req_mask & ETH_PARAMS_IM_COALESCE) {
+ ret = ethnl_get_coalesce(dev, &coalesce);
+ if (ret < 0) {
+ warn_partial_info(info);
+ req_mask &= ~ETH_PARAMS_IM_COALESCE;
+ }
+ }
+ if (req_mask & ETH_PARAMS_IM_RING) {
+ ret = ethnl_get_ring(dev, &ring);
+ if (ret < 0) {
+ warn_partial_info(info);
+ req_mask &= ~ETH_PARAMS_IM_RING;
+ }
+ }
+ if (req_mask & ETH_PARAMS_IM_PAUSE) {
+ ret = ethnl_get_pause(dev, &pause);
+ if (ret < 0) {
+ warn_partial_info(info);
+ req_mask &= ~ETH_PARAMS_IM_PAUSE;
+ }
+ }
+ if (req_mask & ETH_PARAMS_IM_CHANNELS) {
+ ret = ethnl_get_channels(dev, &channels);
+ if (ret < 0) {
+ warn_partial_info(info);
+ req_mask &= ~ETH_PARAMS_IM_CHANNELS;
+ }
+ }
+ if (req_mask & ETH_PARAMS_IM_EEE) {
+ ret = ethnl_get_eee(dev, &eee);
+ if (ret < 0) {
+ warn_partial_info(info);
+ req_mask &= ~ETH_PARAMS_IM_EEE;
+ }
+ }
+ if (req_mask & ETH_PARAMS_IM_FEC) {
+ ret = ethnl_get_fec(dev, &fec);
+ if (ret < 0) {
+ warn_partial_info(info);
+ req_mask &= ~ETH_PARAMS_IM_FEC;
+ }
+ }
+
+ ret = ethnl_params_size(&eee, req_flags, req_mask);
+ if (ret < 0)
+ goto err_putdev;
+ else
+ reply_len = ret;
+ rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_CMD_SET_PARAMS, info,
+ &ehdr);
+ ret = -ENOMEM;
+ if (!rskb)
+ goto err_putdev;
+ if (req_mask != ETH_SETTINGS_IM_DEFAULT)
+ ehdr->info_mask = req_mask;
+
+ if (req_mask & ETH_PARAMS_IM_COALESCE) {
+ ret = fill_coalesce(rskb, &coalesce);
+ if (ret < 0)
+ goto err;
+ }
+ if (req_mask & ETH_PARAMS_IM_RING) {
+ ret = fill_ring(rskb, &ring);
+ if (ret < 0)
+ goto err;
+ }
+ if (req_mask & ETH_PARAMS_IM_PAUSE) {
+ ret = fill_pause(rskb, &pause);
+ if (ret < 0)
+ goto err;
+ }
+ if (req_mask & ETH_PARAMS_IM_CHANNELS) {
+ ret = fill_channels(rskb, &channels);
+ if (ret < 0)
+ goto err;
+ }
+ if (req_mask & ETH_PARAMS_IM_EEE) {
+ ret = fill_eee(rskb, &eee, req_flags);
+ if (ret < 0)
+ goto err;
+ }
+ if (req_mask & ETH_PARAMS_IM_FEC) {
+ ret = fill_fec(rskb, &fec);
+ if (ret < 0)
+ goto err;
+ }
+
+ dev_put(dev);
+ genlmsg_end(rskb, ehdr);
+ return genlmsg_reply(rskb, info);
+
+err:
+ nlmsg_free(rskb);
+err_putdev:
+ dev_put(dev);
+ if (ret == -EMSGSIZE)
+ GENL_SET_ERR_MSG(info,
+ "kernel error, see kernel log for details");
+ WARN_ONCE(ret == -EMSGSIZE,
+ "calculated message payload length (%d) not sufficient\n",
+ reply_len);
+ return ret;
+}
+
/* genetlink paperwork */

static const struct genl_ops ethtool_genl_ops[] = {
@@ -1455,6 +1862,10 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = settings_policy,
.doit = ethnl_set_settings,
},
+ {
+ .cmd = ETHTOOL_CMD_GET_PARAMS,
+ .doit = ethnl_get_params,
+ },
};

static struct genl_family ethtool_genl_family = {
--
2.15.1