Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753335AbdLKNyj (ORCPT ); Mon, 11 Dec 2017 08:54:39 -0500 Received: from mx2.suse.de ([195.135.220.15]:60317 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753318AbdLKNyd (ORCPT ); Mon, 11 Dec 2017 08:54:33 -0500 Message-Id: <2b9543671158e63659570b019f4450de5189cbdd.1513000306.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [RFC PATCH 8/9] ethtool: implement GET_PARAMS message To: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Date: Mon, 11 Dec 2017 14:54:31 +0100 (CET) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 22063 Lines: 723 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 --- 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