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 <mkube...@suse.cz>
---
 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

Reply via email to