lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 30 Jul 2018 14:53:57 +0200 (CEST)
From:   Michal Kubecek <mkubecek@...e.cz>
To:     netdev@...r.kernel.org
Cc:     linux-kernel@...r.kernel.org, Jiri Pirko <jiri@...nulli.us>,
        David Miller <davem@...emloft.net>,
        Florian Fainelli <f.fainelli@...il.com>,
        Roopa Prabhu <roopa@...ulusnetworks.com>,
        Jakub Kicinski <kubakici@...pl>,
        "John W. Linville" <linville@...driver.com>
Subject: [RFC PATCH net-next v2 15/17] ethtool: implement GET_PARAMS message

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@...e.cz>
---
 Documentation/networking/ethtool-netlink.txt | 107 +++-
 include/uapi/linux/ethtool_netlink.h         | 119 ++++
 net/ethtool/Makefile                         |   2 +-
 net/ethtool/netlink.c                        |  10 +
 net/ethtool/params.c                         | 539 +++++++++++++++++++
 5 files changed, 770 insertions(+), 7 deletions(-)
 create mode 100644 net/ethtool/params.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 307d8c6c6c85..630eebd7e741 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -125,6 +125,8 @@ List of message types
     ETHNL_CMD_SET_DRVINFO		response only
     ETHNL_CMD_GET_SETTINGS
     ETHNL_CMD_SET_SETTINGS
+    ETHNL_CMD_GET_PARAMS
+    ETHNL_CMD_SET_PARAMS		response only (for now)
 
 All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
 to indicate the type.
@@ -314,6 +316,99 @@ the operation); mask shows bits which have been changed and value their new
 values.
 
 
+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.
+
+Request contents:
+
+    ETHA_PARAMS_DEV		(nested)	device identification
+    ETHA_PARAMS_INFOMASK	(u32)		info mask
+    ETHA_PARAMS_COMPACT		(flag)		request compact bitsets
+
+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
+
+Response contents: On top level, there is one attribute for each of the
+information categories, the information is nested in it.
+
+    ETHA_PARAMS_DEV		(nested)	device identification
+    ETHA_PARAMS_COALESCE	(nested)	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		(nested)	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		(nested)	pause parameters
+        ETHA_PAUSE_AUTONEG			(bool)
+        ETHA_PAUSE_RX				(bool)
+        ETHA_PAUSE_TX				(bool)
+    ETHA_PARAMS_CHANNELS	(nested)	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		(nested)	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		(nested)	FEC parameters
+        ETHA_FEC_MODES				(bitfield32)
+		- active (value) and configured (selector) FEC encodings
+
+GET_PARAMS requests allow dumps and messages in the same format as response
+to them are broadcasted as notifications on change of these settings using
+netlink or ioctl ethtool interface.
+
+
+
 Request translation
 -------------------
 
@@ -335,11 +430,11 @@ ETHTOOL_NWAY_RST		n/a
 ETHTOOL_GLINK			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_GEEPROM			n/a
 ETHTOOL_SEEPROM			n/a
-ETHTOOL_GCOALESCE		n/a
+ETHTOOL_GCOALESCE		ETHNL_CMD_GET_PARAMS
 ETHTOOL_SCOALESCE		n/a
-ETHTOOL_GRINGPARAM		n/a
+ETHTOOL_GRINGPARAM		ETHNL_CMD_GET_PARAMS
 ETHTOOL_SRINGPARAM		n/a
-ETHTOOL_GPAUSEPARAM		n/a
+ETHTOOL_GPAUSEPARAM		ETHNL_CMD_GET_PARAMS
 ETHTOOL_SPAUSEPARAM		n/a
 ETHTOOL_GRXCSUM			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SRXCSUM			ETHNL_CMD_SET_SETTINGS
@@ -381,7 +476,7 @@ ETHTOOL_GRXFHINDIR		n/a
 ETHTOOL_SRXFHINDIR		n/a
 ETHTOOL_GFEATURES		ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SFEATURES		n/a
-ETHTOOL_GCHANNELS		n/a
+ETHTOOL_GCHANNELS		ETHNL_CMD_GET_PARAMS
 ETHTOOL_SCHANNELS		n/a
 ETHTOOL_SET_DUMP		n/a
 ETHTOOL_GET_DUMP_FLAG		n/a
@@ -389,7 +484,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			ETHNL_CMD_GET_PARAMS
 ETHTOOL_SEEE			n/a
 ETHTOOL_GRSSH			n/a
 ETHTOOL_SRSSH			n/a
@@ -401,6 +496,6 @@ ETHTOOL_GLINKSETTINGS		ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SLINKSETTINGS		ETHNL_CMD_SET_SETTINGS
 ETHTOOL_PHY_GTUNABLE		n/a
 ETHTOOL_PHY_STUNABLE		n/a
-ETHTOOL_GFECPARAM		n/a
+ETHTOOL_GFECPARAM		ETHNL_CMD_GET_PARAMS
 ETHTOOL_SFECPARAM		n/a
 
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 8dfcb9ef4009..fec0f2000dc5 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -14,6 +14,8 @@ enum {
 	ETHNL_CMD_SET_DRVINFO,		/* only for reply */
 	ETHNL_CMD_GET_SETTINGS,
 	ETHNL_CMD_SET_SETTINGS,
+	ETHNL_CMD_GET_PARAMS,
+	ETHNL_CMD_SET_PARAMS,
 
 	__ETHNL_CMD_MAX,
 	ETHNL_CMD_MAX = (__ETHNL_CMD_MAX - 1)
@@ -197,6 +199,123 @@ enum {
 	ETHA_FEATURES_MAX = (__ETHA_FEATURES_MAX - 1)
 };
 
+/* GET_PARAMS / SET_PARAMS */
+
+enum {
+	ETHA_PARAMS_UNSPEC,
+	ETHA_PARAMS_DEV,			/* nest - ETHA_DEV_* */
+	ETHA_PARAMS_INFOMASK,			/* u32 */
+	ETHA_PARAMS_COMPACT,			/* flag */
+	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_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/ethtool/Makefile b/net/ethtool/Makefile
index 8dd98310ed6f..10aeedcee336 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -4,4 +4,4 @@ obj-y				+= ioctl.o common.o
 
 obj-$(CONFIG_ETHTOOL_NETLINK)	+= ethtool_nl.o
 
-ethtool_nl-y	:= netlink.o strset.o drvinfo.o settings.o
+ethtool_nl-y	:= netlink.o strset.o drvinfo.o settings.o params.o
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 6e183caca01f..721101ed2ab6 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -731,13 +731,16 @@ int ethnl_get_strset(struct sk_buff *skb, struct genl_info *info);
 int ethnl_get_drvinfo(struct sk_buff *skb, struct genl_info *info);
 int ethnl_get_settings(struct sk_buff *skb, struct genl_info *info);
 int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info);
+int ethnl_get_params(struct sk_buff *skb, struct genl_info *info);
 
 int ethnl_strset_start(struct netlink_callback *cb);
 int ethnl_drvinfo_start(struct netlink_callback *cb);
 int ethnl_settings_start(struct netlink_callback *cb);
+int ethnl_params_start(struct netlink_callback *cb);
 
 int ethnl_strset_done(struct netlink_callback *cb);
 int ethnl_settings_done(struct netlink_callback *cb);
+int ethnl_params_done(struct netlink_callback *cb);
 
 static const struct genl_ops ethtool_genl_ops[] = {
 	{
@@ -765,6 +768,13 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.flags	= GENL_UNS_ADMIN_PERM,
 		.doit	= ethnl_set_settings,
 	},
+	{
+		.cmd	= ETHNL_CMD_GET_PARAMS,
+		.doit	= ethnl_get_params,
+		.start	= ethnl_params_start,
+		.dumpit	= ethnl_dumpit,
+		.done	= ethnl_params_done,
+	},
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/params.c b/net/ethtool/params.c
new file mode 100644
index 000000000000..07d4c527abf2
--- /dev/null
+++ b/net/ethtool/params.c
@@ -0,0 +1,539 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#include "netlink.h"
+#include "common.h"
+
+static const struct nla_policy params_policy[ETHA_PARAMS_MAX + 1] = {
+	[ETHA_PARAMS_UNSPEC]		= { .type = NLA_UNSPEC },
+	[ETHA_PARAMS_DEV]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_INFOMASK]		= { .type = NLA_U32 },
+	[ETHA_PARAMS_COMPACT]		= { .type = NLA_FLAG },
+	[ETHA_PARAMS_COALESCE]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_RING]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_PAUSE]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_CHANNELS]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_EEE]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_FEC]		= { .type = NLA_NESTED },
+};
+
+struct params_data {
+	struct ethtool_coalesce		coalesce;
+	struct ethtool_channels		channels;
+	struct ethtool_pauseparam	pause;
+	struct ethtool_ringparam	ring;
+	struct ethtool_eee		eee;
+	struct ethtool_fecparam		fec;
+	u32				req_mask;
+};
+
+struct params_reqinfo {
+	struct net_device		*dev;
+	u32				req_mask;
+	bool				compact;
+	bool				have_rtnl;
+};
+
+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 params_size(struct params_data *data,
+		       struct params_reqinfo *req_info)
+{
+	struct ethtool_eee *eee = &data->eee;
+	u32 req_mask = req_info->req_mask;
+	bool compact = req_info->compact;
+	int len = 0;
+
+	if (req_mask & ETH_PARAMS_IM_COALESCE)
+		len += nla_total_size(20 * nla_total_size(sizeof(u32)) +
+				      2 * nla_total_size(sizeof(u8)));
+	if (req_mask & ETH_PARAMS_IM_RING)
+		len += nla_total_size(8 * nla_total_size(sizeof(u32)));
+	if (req_mask & ETH_PARAMS_IM_PAUSE)
+		len += nla_total_size(3 * nla_total_size(sizeof(u8)));
+	if (req_mask & ETH_PARAMS_IM_CHANNELS)
+		len += nla_total_size(8 * nla_total_size(sizeof(u32)));
+	if (req_mask & ETH_PARAMS_IM_EEE) {
+		int nlen = 0;
+		int ret;
+
+		/* link_modes */
+		ret = ethnl_bitset32_size(compact, sizeof(u32) * 8,
+					  &eee->advertised, &eee->supported,
+					  link_mode_names);
+		if (ret < 0)
+			return ret;
+		nlen += ret;
+		/* peer_modes */
+		ret = ethnl_bitset32_size(compact, 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 (req_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, bool compact)
+{
+	struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_EEE);
+	int ret;
+
+	if (!nest)
+		return -EMSGSIZE;
+	ret = ethnl_put_bitset32(skb, ETHA_EEE_LINK_MODES, compact,
+				 sizeof(data->advertised) * 8,
+				 &data->advertised, &data->supported,
+				 link_mode_names);
+	if (ret < 0)
+		goto err;
+	ret = ethnl_put_bitset32(skb, ETHA_EEE_PEER_MODES, compact,
+				 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 parse_params_req(struct params_reqinfo *req_info,
+			    struct genl_info *info, struct sk_buff *skb,
+			    const struct nlmsghdr *nlhdr)
+{
+	struct nlattr *tb[ETHA_PARAMS_MAX + 1];
+	int ret;
+
+	memset(req_info, '\0', sizeof(*req_info));
+
+	ret = genlmsg_parse(nlhdr, &ethtool_genl_family, tb,
+			    ETHA_PARAMS_MAX, params_policy,
+			    info ? info->extack : NULL);
+	if (ret < 0)
+		return ret;
+
+	if (tb[ETHA_PARAMS_DEV]) {
+		req_info->dev = ethnl_dev_get(info, tb[ETHA_PARAMS_DEV]);
+		if (IS_ERR(req_info->dev)) {
+			ret = PTR_ERR(req_info->dev);
+			req_info->dev = NULL;
+			return ret;
+		}
+	}
+	if (tb[ETHA_PARAMS_INFOMASK])
+		req_info->req_mask = nla_get_u32(tb[ETHA_PARAMS_INFOMASK]);
+	if (tb[ETHA_PARAMS_COMPACT])
+		req_info->compact = true;
+	if (req_info->req_mask == 0)
+		req_info->req_mask = ETH_PARAMS_IM_DEFAULT;
+
+	return 0;
+}
+
+static int prepare_params(struct params_data *data,
+			  struct params_reqinfo *req_info,
+			  struct genl_info *info, struct net_device *dev)
+{
+	u32 req_mask = req_info->req_mask;
+	int ret;
+
+	memset(data, '\0', sizeof(*data));
+	if (!req_info->have_rtnl)
+		rtnl_lock();
+	if (req_mask & ETH_PARAMS_IM_COALESCE) {
+		ret = ethnl_get_coalesce(dev, &data->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, &data->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, &data->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, &data->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, &data->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, &data->fec);
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~ETH_PARAMS_IM_FEC;
+		}
+	}
+	if (!req_info->have_rtnl)
+		rtnl_unlock();
+
+	data->req_mask = req_mask;
+	return 0;
+}
+
+static int fill_params(struct sk_buff *rskb, struct params_data *data,
+		       struct params_reqinfo *req_info)
+{
+	u32 req_mask = data->req_mask;
+	int ret;
+
+	if (req_mask & ETH_PARAMS_IM_COALESCE) {
+		ret = fill_coalesce(rskb, &data->coalesce);
+		if (ret < 0)
+			return ret;
+	}
+	if (req_mask & ETH_PARAMS_IM_RING) {
+		ret = fill_ring(rskb, &data->ring);
+		if (ret < 0)
+			return ret;
+	}
+	if (req_mask & ETH_PARAMS_IM_PAUSE) {
+		ret = fill_pause(rskb, &data->pause);
+		if (ret < 0)
+			return ret;
+	}
+	if (req_mask & ETH_PARAMS_IM_CHANNELS) {
+		ret = fill_channels(rskb, &data->channels);
+		if (ret < 0)
+			return ret;
+	}
+	if (req_mask & ETH_PARAMS_IM_EEE) {
+		ret = fill_eee(rskb, &data->eee, req_info->compact);
+		if (ret < 0)
+			return ret;
+	}
+	if (req_mask & ETH_PARAMS_IM_FEC) {
+		ret = fill_fec(rskb, &data->fec);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+int ethnl_get_params(struct sk_buff *skb, struct genl_info *info)
+{
+	struct params_data data;
+	struct params_reqinfo req_info;
+	struct sk_buff *rskb;
+	int reply_len;
+	void *ehdr;
+	int ret;
+
+	ret = parse_params_req(&req_info, info, skb, info->nlhdr);
+	if (ret < 0)
+		goto err_dev;
+	ret = prepare_params(&data, &req_info, info, req_info.dev);
+	if (ret < 0)
+		goto err_dev;
+	reply_len = params_size(&data, &req_info);
+	if (ret < 0)
+		goto err_dev;
+	ret = -ENOMEM;
+	rskb = ethnl_reply_init(reply_len, req_info.dev, ETHNL_CMD_SET_PARAMS,
+				ETHA_PARAMS_DEV, info, &ehdr);
+	if (!rskb)
+		goto err_dev;
+	ret = fill_params(rskb, &data, &req_info);
+	if (ret < 0)
+		goto err;
+
+	genlmsg_end(rskb, ehdr);
+	dev_put(req_info.dev);
+	return genlmsg_reply(rskb, info);
+
+err:
+	WARN_ONCE(ret == -EMSGSIZE,
+		  "calculated message payload length (%d) not sufficient\n",
+		  reply_len);
+	nlmsg_free(rskb);
+err_dev:
+	if (req_info.dev)
+		dev_put(req_info.dev);
+	return ret;
+}
+
+static int params_dump(struct sk_buff *skb, struct netlink_callback *cb,
+		       struct net_device *dev)
+{
+	struct params_data data;
+	struct params_reqinfo *req_info;
+	int ret;
+
+	req_info = (struct params_reqinfo *)cb->args[4];
+	ret = prepare_params(&data, req_info, NULL, dev);
+	if (ret < 0)
+		return ret;
+	ret = ethnl_fill_dev(skb, dev, ETHA_PARAMS_DEV);
+	if (ret < 0)
+		return ret;
+	ret = fill_params(skb, &data, req_info);
+	return ret;
+}
+
+int ethnl_params_start(struct netlink_callback *cb)
+{
+	struct params_reqinfo *req_info;
+	int ret;
+
+	req_info = kmalloc(sizeof(*req_info), GFP_KERNEL);
+	if (!req_info)
+		return -ENOMEM;
+	ret = parse_params_req(req_info, NULL, cb->skb, cb->nlh);
+	if (ret < 0) {
+		if (req_info->dev)
+			dev_put(req_info->dev);
+		req_info->dev = NULL;
+		return ret;
+	}
+
+	cb->args[0] = (long)params_dump;
+	cb->args[1] = ETHNL_CMD_SET_PARAMS;
+	cb->args[4] = (long)req_info;
+
+	return 0;
+}
+
+int ethnl_params_done(struct netlink_callback *cb)
+{
+	struct params_reqinfo *req_info;
+
+	req_info = (struct params_reqinfo *)cb->args[4];
+	if (req_info->dev)
+		dev_put(req_info->dev);
+	kfree(req_info);
+
+	return 0;
+}
-- 
2.18.0

Powered by blists - more mailing lists