linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michal Kubecek <mkubecek@suse.cz>
To: David Miller <davem@davemloft.net>, netdev@vger.kernel.org
Cc: Jakub Kicinski <jakub.kicinski@netronome.com>,
	Jiri Pirko <jiri@resnulli.us>, Andrew Lunn <andrew@lunn.ch>,
	Florian Fainelli <f.fainelli@gmail.com>,
	John Linville <linville@tuxdriver.com>,
	Stephen Hemminger <stephen@networkplumber.org>,
	Johannes Berg <johannes@sipsolutions.net>,
	linux-kernel@vger.kernel.org
Subject: [PATCH net-next v6 12/15] ethtool: provide link settings and link modes in SETTINGS_GET request
Date: Tue,  2 Jul 2019 13:50:39 +0200 (CEST)	[thread overview]
Message-ID: <6a734345f85fb2010015303ee14941cdeea9559a.1562067622.git.mkubecek@suse.cz> (raw)
In-Reply-To: <cover.1562067622.git.mkubecek@suse.cz>

Implement SETTINGS_GET netlink request to get link settings and link mode
information provided by ETHTOOL_GLINKSETTINGS and ETHTOOL_GSET ioctl
commands.

The information in SETTINGS_GET_REPLY message sent as reply is divided into
two parts and client can use info mask in request header to select only
one of them:

  - ETHTOOL_IM_SETTINGS_LINKINFO: physical port, phy MDIO address, MDI(-X)
    status, MDI(-X) control and transceiver
  - ETHTOOL_IM_SETTINGS_LINKMODES: supported and advertised link modes,
    autonegotiation state, link speed and duplex

SETTINGS_GET request can be used with NLM_F_DUMP (and without device
identification) to request the information for all devices in current
network namespace providing the data.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  48 +++-
 include/linux/ethtool_netlink.h              |   3 +
 include/uapi/linux/ethtool_netlink.h         |  49 ++++
 net/ethtool/Makefile                         |   2 +-
 net/ethtool/common.c                         |  48 ++++
 net/ethtool/common.h                         |   4 +
 net/ethtool/ioctl.c                          |  48 ----
 net/ethtool/netlink.c                        |   8 +
 net/ethtool/netlink.h                        |   1 +
 net/ethtool/settings.c                       | 259 +++++++++++++++++++
 10 files changed, 419 insertions(+), 51 deletions(-)
 create mode 100644 net/ethtool/settings.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 21e5030734aa..1d803488e02c 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -151,10 +151,12 @@ according to message purpose:
 Userspace to kernel:
 
     ETHTOOL_MSG_STRSET_GET		get string set
+    ETHTOOL_MSG_SETTINGS_GET		get device settings
 
 Kernel to userspace:
 
     ETHTOOL_MSG_STRSET_GET_REPLY	string set contents
+    ETHTOOL_MSG_SETTINGS_GET_REPLY	device settings
 
 "GET" requests are sent by userspace applications to retrieve device
 information. They usually do not contain any message specific attributes.
@@ -231,6 +233,48 @@ Flag ETHTOOL_A_STRSET_COUNTS tells kernel to only return string counts of the
 sets, not the actual strings.
 
 
+SETTINGS_GET
+------------
+
+SETTINGS_GET request retrieves information provided by ETHTOOL_GLINKSETTINGS,
+ETHTOOL_GWOL, ETHTOOL_GMSGLVL and ETHTOOL_GLINK ioctl requests. The request
+doesn't use any attributes.
+
+Request attributes:
+
+    ETHTOOL_A_SETTINGS_HEADER		(nested)	request header
+
+Info mask bits meaning:
+
+    ETHTOOL_IM_SETTINGS_LINKINFO	link settings
+    ETHTOOL_IM_SETTINGS_LINKMODES	link modes and related
+
+Response contents:
+
+    ETHTOOL_A_SETTINGS_HEADER		(nested)	reply header
+    ETHTOOL_A_SETTINGS_LINK_INFO	(nested)	link settings
+        ETHTOOL_A_LINKINFO_PORT		    (u8)	    physical port
+        ETHTOOL_A_LINKINFO_PHYADDR	    (u8)	    phy MDIO address
+        ETHTOOL_A_LINKINFO_TP_MDIX	    (u8)	    MDI(-X) status
+        ETHTOOL_A_LINKINFO_TP_MDIX_CTRL	    (u8)	    MDI(-X) control
+        ETHTOOL_A_LINKINFO_TRANSCEIVER	    (u8)	    transceiver
+    ETHTOOL_A_SETTINGS_LINK_MODES	(nested)	link modes
+        ETHTOOL_A_LINKMODES_AUTONEG	    (u8)	    autonegotiation status
+        ETHTOOL_A_LINKMODES_OURS	    (bitset)	    advertised link modes
+        ETHTOOL_A_LINKMODES_PEER	    (bitset)	    partner link modes
+        ETHTOOL_A_LINKMODES_SPEED	    (u32)	    link speed (Mb/s)
+        ETHTOOL_A_LINKMODES_DUPLEX	    (u8)	    duplex mode
+
+Most of the attributes and their values have the same meaning as matching
+members of the corresponding ioctl structures. For ETHTOOL_A_LINKMODES_OURS,
+value represents advertised modes and mask represents supported modes.
+ETHTOOL_A_LINKMODES_PEER in the reply is a bit list.
+
+SETTINGS_GET 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
 -------------------
 
@@ -240,7 +284,7 @@ have their netlink replacement yet.
 
 ioctl command			netlink command
 ---------------------------------------------------------------------
-ETHTOOL_GSET			n/a
+ETHTOOL_GSET			ETHTOOL_MSG_SETTINGS_GET
 ETHTOOL_SSET			n/a
 ETHTOOL_GDRVINFO		n/a
 ETHTOOL_GREGS			n/a
@@ -314,7 +358,7 @@ ETHTOOL_GTUNABLE		n/a
 ETHTOOL_STUNABLE		n/a
 ETHTOOL_GPHYSTATS		n/a
 ETHTOOL_PERQUEUE		n/a
-ETHTOOL_GLINKSETTINGS		n/a
+ETHTOOL_GLINKSETTINGS		ETHTOOL_MSG_SETTINGS_GET
 ETHTOOL_SLINKSETTINGS		n/a
 ETHTOOL_PHY_GTUNABLE		n/a
 ETHTOOL_PHY_STUNABLE		n/a
diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h
index 2a15e64a16f3..e770e6e9acca 100644
--- a/include/linux/ethtool_netlink.h
+++ b/include/linux/ethtool_netlink.h
@@ -7,6 +7,9 @@
 #include <linux/ethtool.h>
 #include <linux/netdevice.h>
 
+#define __ETHTOOL_LINK_MODE_MASK_NWORDS \
+	DIV_ROUND_UP(__ETHTOOL_LINK_MODE_MASK_NBITS, 32)
+
 enum ethtool_multicast_groups {
 	ETHNL_MCGRP_MONITOR,
 };
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 11b8519d2c1d..a046dd8da50e 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -15,6 +15,7 @@
 enum {
 	ETHTOOL_MSG_USER_NONE,
 	ETHTOOL_MSG_STRSET_GET,
+	ETHTOOL_MSG_SETTINGS_GET,
 
 	/* add new constants above here */
 	__ETHTOOL_MSG_USER_CNT,
@@ -25,6 +26,7 @@ enum {
 enum {
 	ETHTOOL_MSG_KERNEL_NONE,
 	ETHTOOL_MSG_STRSET_GET_REPLY,
+	ETHTOOL_MSG_SETTINGS_GET_REPLY,
 
 	/* add new constants above here */
 	__ETHTOOL_MSG_KERNEL_CNT,
@@ -147,6 +149,53 @@ enum {
 
 #define ETHTOOL_RF_STRSET_ALL (ETHTOOL_RF_STRSET_COUNTS)
 
+/* SETTINGS */
+
+enum {
+	ETHTOOL_A_SETTINGS_UNSPEC,
+	ETHTOOL_A_SETTINGS_HEADER,		/* nest - _A_HEADER_* */
+	ETHTOOL_A_SETTINGS_LINK_INFO,		/* nest - _A_LINKINFO_* */
+	ETHTOOL_A_SETTINGS_LINK_MODES,		/* nest - _A_LINKMODES_* */
+
+	/* add new constants above here */
+	__ETHTOOL_A_SETTINGS_CNT,
+	ETHTOOL_A_SETTINGS_MAX = (__ETHTOOL_A_SETTINGS_CNT - 1)
+};
+
+#define ETHTOOL_IM_SETTINGS_LINKINFO		(1U << 0)
+#define ETHTOOL_IM_SETTINGS_LINKMODES		(1U << 1)
+
+#define ETHTOOL_IM_SETTINGS_ALL (ETHTOOL_IM_SETTINGS_LINKINFO | \
+				 ETHTOOL_IM_SETTINGS_LINKMODES)
+
+#define ETHTOOL_RF_SETTINGS_ALL 0
+
+enum {
+	ETHTOOL_A_LINKINFO_UNSPEC,
+	ETHTOOL_A_LINKINFO_PORT,		/* u8 */
+	ETHTOOL_A_LINKINFO_PHYADDR,		/* u8 */
+	ETHTOOL_A_LINKINFO_TP_MDIX,		/* u8 */
+	ETHTOOL_A_LINKINFO_TP_MDIX_CTRL,	/* u8 */
+	ETHTOOL_A_LINKINFO_TRANSCEIVER,		/* u8 */
+
+	/* add new constants above here */
+	__ETHTOOL_A_LINKINFO_CNT,
+	ETHTOOL_A_LINKINFO_MAX = (__ETHTOOL_A_LINKINFO_CNT - 1)
+};
+
+enum {
+	ETHTOOL_A_LINKMODES_UNSPEC,
+	ETHTOOL_A_LINKMODES_AUTONEG,		/* u8 */
+	ETHTOOL_A_LINKMODES_OURS,		/* bitset */
+	ETHTOOL_A_LINKMODES_PEER,		/* bitset */
+	ETHTOOL_A_LINKMODES_SPEED,		/* u32 */
+	ETHTOOL_A_LINKMODES_DUPLEX,		/* u8 */
+
+	/* add new constants above here */
+	__ETHTOOL_A_LINKMODES_CNT,
+	ETHTOOL_A_LINKMODES_MAX = (__ETHTOOL_A_LINKMODES_CNT - 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 11ceb00821b3..1155e5e9ef69 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 bitset.o strset.o
+ethtool_nl-y	:= netlink.o bitset.o strset.o settings.o
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index b0ce420e994e..abb00b3a7e77 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -82,3 +82,51 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
 	[ETHTOOL_PHY_DOWNSHIFT]	= "phy-downshift",
 	[ETHTOOL_PHY_FAST_LINK_DOWN] = "phy-fast-link-down",
 };
+
+/* return false if legacy contained non-0 deprecated fields
+ * maxtxpkt/maxrxpkt. rest of ksettings always updated
+ */
+bool
+convert_legacy_settings_to_link_ksettings(
+	struct ethtool_link_ksettings *link_ksettings,
+	const struct ethtool_cmd *legacy_settings)
+{
+	bool retval = true;
+
+	memset(link_ksettings, 0, sizeof(*link_ksettings));
+
+	/* This is used to tell users that driver is still using these
+	 * deprecated legacy fields, and they should not use
+	 * %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS
+	 */
+	if (legacy_settings->maxtxpkt ||
+	    legacy_settings->maxrxpkt)
+		retval = false;
+
+	ethtool_convert_legacy_u32_to_link_mode(
+		link_ksettings->link_modes.supported,
+		legacy_settings->supported);
+	ethtool_convert_legacy_u32_to_link_mode(
+		link_ksettings->link_modes.advertising,
+		legacy_settings->advertising);
+	ethtool_convert_legacy_u32_to_link_mode(
+		link_ksettings->link_modes.lp_advertising,
+		legacy_settings->lp_advertising);
+	link_ksettings->base.speed
+		= ethtool_cmd_speed(legacy_settings);
+	link_ksettings->base.duplex
+		= legacy_settings->duplex;
+	link_ksettings->base.port
+		= legacy_settings->port;
+	link_ksettings->base.phy_address
+		= legacy_settings->phy_address;
+	link_ksettings->base.autoneg
+		= legacy_settings->autoneg;
+	link_ksettings->base.mdio_support
+		= legacy_settings->mdio_support;
+	link_ksettings->base.eth_tp_mdix
+		= legacy_settings->eth_tp_mdix;
+	link_ksettings->base.eth_tp_mdix_ctrl
+		= legacy_settings->eth_tp_mdix_ctrl;
+	return retval;
+}
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index 41b2efc1e4e1..0381936d8e1e 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -14,4 +14,8 @@ tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN];
 extern const char
 phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
 
+bool convert_legacy_settings_to_link_ksettings(
+	struct ethtool_link_ksettings *link_ksettings,
+	const struct ethtool_cmd *legacy_settings);
+
 #endif /* _ETHTOOL_COMMON_H */
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index b35366dd9997..ed53e07d619e 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -352,54 +352,6 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
 }
 EXPORT_SYMBOL(ethtool_convert_link_mode_to_legacy_u32);
 
-/* return false if legacy contained non-0 deprecated fields
- * maxtxpkt/maxrxpkt. rest of ksettings always updated
- */
-static bool
-convert_legacy_settings_to_link_ksettings(
-	struct ethtool_link_ksettings *link_ksettings,
-	const struct ethtool_cmd *legacy_settings)
-{
-	bool retval = true;
-
-	memset(link_ksettings, 0, sizeof(*link_ksettings));
-
-	/* This is used to tell users that driver is still using these
-	 * deprecated legacy fields, and they should not use
-	 * %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS
-	 */
-	if (legacy_settings->maxtxpkt ||
-	    legacy_settings->maxrxpkt)
-		retval = false;
-
-	ethtool_convert_legacy_u32_to_link_mode(
-		link_ksettings->link_modes.supported,
-		legacy_settings->supported);
-	ethtool_convert_legacy_u32_to_link_mode(
-		link_ksettings->link_modes.advertising,
-		legacy_settings->advertising);
-	ethtool_convert_legacy_u32_to_link_mode(
-		link_ksettings->link_modes.lp_advertising,
-		legacy_settings->lp_advertising);
-	link_ksettings->base.speed
-		= ethtool_cmd_speed(legacy_settings);
-	link_ksettings->base.duplex
-		= legacy_settings->duplex;
-	link_ksettings->base.port
-		= legacy_settings->port;
-	link_ksettings->base.phy_address
-		= legacy_settings->phy_address;
-	link_ksettings->base.autoneg
-		= legacy_settings->autoneg;
-	link_ksettings->base.mdio_support
-		= legacy_settings->mdio_support;
-	link_ksettings->base.eth_tp_mdix
-		= legacy_settings->eth_tp_mdix;
-	link_ksettings->base.eth_tp_mdix_ctrl
-		= legacy_settings->eth_tp_mdix_ctrl;
-	return retval;
-}
-
 /* return false if ksettings link modes had higher bits
  * set. legacy_settings always updated (best effort)
  */
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index cba1f2259248..6c0cfa9001a1 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -280,6 +280,7 @@ struct ethnl_dump_ctx {
 
 static const struct get_request_ops *get_requests[__ETHTOOL_MSG_USER_CNT] = {
 	[ETHTOOL_MSG_STRSET_GET]	= &strset_request_ops,
+	[ETHTOOL_MSG_SETTINGS_GET]	= &settings_request_ops,
 };
 
 static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -632,6 +633,13 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.dumpit	= ethnl_get_dumpit,
 		.done	= ethnl_get_done,
 	},
+	{
+		.cmd	= ETHTOOL_MSG_SETTINGS_GET,
+		.doit	= ethnl_get_doit,
+		.start	= ethnl_get_start,
+		.dumpit	= ethnl_get_dumpit,
+		.done	= ethnl_get_done,
+	},
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index d85b1edc1b91..27832a3956c8 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -294,5 +294,6 @@ struct get_request_ops {
 /* request handlers */
 
 extern const struct get_request_ops strset_request_ops;
+extern const struct get_request_ops settings_request_ops;
 
 #endif /* _NET_ETHTOOL_NETLINK_H */
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
new file mode 100644
index 000000000000..11ec30b9d48b
--- /dev/null
+++ b/net/ethtool/settings.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+
+#include "netlink.h"
+#include "common.h"
+#include "bitset.h"
+
+struct settings_data {
+	struct ethnl_req_info		reqinfo_base;
+
+	/* everything below here will be reset for each device in dumps */
+	struct ethnl_reply_data		repdata_base;
+	struct ethtool_link_ksettings	ksettings;
+	struct ethtool_link_settings	*lsettings;
+	bool				lpm_empty;
+};
+
+static const struct nla_policy
+settings_get_policy[ETHTOOL_A_SETTINGS_MAX + 1] = {
+	[ETHTOOL_A_SETTINGS_UNSPEC]	= { .type = NLA_REJECT },
+	[ETHTOOL_A_SETTINGS_HEADER]	= { .type = NLA_NESTED },
+	[ETHTOOL_A_SETTINGS_LINK_INFO]	= { .type = NLA_REJECT },
+	[ETHTOOL_A_SETTINGS_LINK_MODES]	= { .type = NLA_REJECT },
+};
+
+static int ethnl_get_link_ksettings(struct genl_info *info,
+				    struct net_device *dev,
+				    struct ethtool_link_ksettings *ksettings)
+{
+	int ret;
+
+	ret = __ethtool_get_link_ksettings(dev, ksettings);
+
+	if (ret < 0 && info)
+		GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
+	return ret;
+}
+
+/* prepare_data() handler */
+static int settings_prepare(struct ethnl_req_info *req_info,
+			    struct genl_info *info)
+{
+	struct settings_data *data =
+		container_of(req_info, struct settings_data, reqinfo_base);
+	struct net_device *dev = data->repdata_base.dev;
+	u32 req_mask = req_info->req_mask;
+	int ret;
+
+	data->lsettings = &data->ksettings.base;
+	data->lpm_empty = true;
+
+	ret = ethnl_before_ops(dev);
+	if (ret < 0)
+		return ret;
+	if (req_mask &
+	    (ETHTOOL_IM_SETTINGS_LINKINFO | ETHTOOL_IM_SETTINGS_LINKMODES)) {
+		ret = ethnl_get_link_ksettings(info, dev, &data->ksettings);
+		if (ret < 0)
+			req_mask &= ~(ETHTOOL_IM_SETTINGS_LINKINFO |
+				      ETHTOOL_IM_SETTINGS_LINKMODES);
+	}
+	if (req_mask & ETHTOOL_IM_SETTINGS_LINKMODES) {
+		data->lpm_empty =
+			bitmap_empty(data->ksettings.link_modes.lp_advertising,
+				     __ETHTOOL_LINK_MODE_MASK_NBITS);
+		ethnl_bitmap_to_u32(data->ksettings.link_modes.supported,
+				    __ETHTOOL_LINK_MODE_MASK_NWORDS);
+		ethnl_bitmap_to_u32(data->ksettings.link_modes.advertising,
+				    __ETHTOOL_LINK_MODE_MASK_NWORDS);
+		ethnl_bitmap_to_u32(data->ksettings.link_modes.lp_advertising,
+				    __ETHTOOL_LINK_MODE_MASK_NWORDS);
+	}
+	ethnl_after_ops(dev);
+
+	data->repdata_base.info_mask = req_mask;
+	if (req_info->req_mask & ~req_mask && info)
+		GENL_SET_ERR_MSG(info,
+				 "not all requested data could be retrieved");
+	return 0;
+}
+
+static int settings_linkinfo_size(void)
+{
+	int len = 0;
+
+	/* port, phyaddr, mdix, mdixctrl, transcvr */
+	len += 5 * nla_total_size(sizeof(u8));
+	/* mdio_support */
+	len += nla_total_size(sizeof(struct nla_bitfield32));
+
+	/* nest */
+	return nla_total_size(len);
+}
+
+static int
+settings_linkmodes_size(const struct ethtool_link_ksettings *ksettings,
+			bool compact)
+{
+	unsigned int flags = compact ? ETHNL_BITSET_COMPACT : 0;
+	u32 *supported = (u32 *)ksettings->link_modes.supported;
+	u32 *advertising = (u32 *)ksettings->link_modes.advertising;
+	u32 *lp_advertising = (u32 *)ksettings->link_modes.lp_advertising;
+	int len = 0, ret;
+
+	/* speed, duplex, autoneg */
+	len += nla_total_size(sizeof(u32)) + 2 * nla_total_size(sizeof(u8));
+	ret = ethnl_bitset32_size(__ETHTOOL_LINK_MODE_MASK_NBITS, advertising,
+				  supported, link_mode_names, flags);
+	if (ret < 0)
+		return ret;
+	len += ret;
+	ret = ethnl_bitset32_size(__ETHTOOL_LINK_MODE_MASK_NBITS,
+				  lp_advertising, NULL, link_mode_names,
+				  flags & ETHNL_BITSET_LIST);
+	if (ret < 0)
+		return ret;
+	len += ret;
+
+	/* nest */
+	return nla_total_size(len);
+}
+
+/* reply_size() handler
+ *
+ * To keep things simple, reserve space for some attributes which may not
+ * be added to the message (e.g. ETHTOOL_A_SETTINGS_SOPASS); therefore the
+ * length returned may be bigger than the actual length of the message sent.
+ */
+static int settings_size(const struct ethnl_req_info *req_info)
+{
+	struct settings_data *data =
+		container_of(req_info, struct settings_data, reqinfo_base);
+	u32 info_mask = data->repdata_base.info_mask;
+	bool compact = req_info->global_flags & ETHTOOL_RF_COMPACT;
+	int len = 0, ret;
+
+	len += ethnl_reply_header_size();
+	if (info_mask & ETHTOOL_IM_SETTINGS_LINKINFO)
+		len += settings_linkinfo_size();
+	if (info_mask & ETHTOOL_IM_SETTINGS_LINKMODES) {
+		ret = settings_linkmodes_size(&data->ksettings, compact);
+		if (ret < 0)
+			return ret;
+		len += ret;
+	}
+
+	return len;
+}
+
+static int settings_fill_linkinfo(struct sk_buff *skb,
+				  const struct ethtool_link_settings *lsettings)
+{
+	struct nlattr *nest;
+
+	nest = nla_nest_start(skb, ETHTOOL_A_SETTINGS_LINK_INFO);
+	if (!nest)
+		return -EMSGSIZE;
+
+	if (nla_put_u8(skb, ETHTOOL_A_LINKINFO_PORT, lsettings->port) ||
+	    nla_put_u8(skb, ETHTOOL_A_LINKINFO_PHYADDR,
+		       lsettings->phy_address) ||
+	    nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX,
+		       lsettings->eth_tp_mdix) ||
+	    nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL,
+		       lsettings->eth_tp_mdix_ctrl) ||
+	    nla_put_u8(skb, ETHTOOL_A_LINKINFO_TRANSCEIVER,
+		       lsettings->transceiver)) {
+		nla_nest_cancel(skb, nest);
+		return -EMSGSIZE;
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+}
+
+static int
+settings_fill_linkmodes(struct sk_buff *skb,
+			const struct ethtool_link_ksettings *ksettings,
+			bool lpm_empty, bool compact)
+{
+	const u32 *supported = (const u32 *)ksettings->link_modes.supported;
+	const u32 *advertising = (const u32 *)ksettings->link_modes.advertising;
+	const u32 *lp_adv = (const u32 *)ksettings->link_modes.lp_advertising;
+	const unsigned int flags = compact ? ETHNL_BITSET_COMPACT : 0;
+	const struct ethtool_link_settings *lsettings = &ksettings->base;
+	struct nlattr *nest;
+	int ret;
+
+	nest = nla_nest_start(skb, ETHTOOL_A_SETTINGS_LINK_MODES);
+	if (!nest)
+		return -EMSGSIZE;
+	if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_AUTONEG, lsettings->autoneg))
+		goto nla_put_failure;
+
+	ret = ethnl_put_bitset32(skb, ETHTOOL_A_LINKMODES_OURS,
+				 __ETHTOOL_LINK_MODE_MASK_NBITS, advertising,
+				 supported, link_mode_names, flags);
+	if (ret < 0)
+		goto nla_put_failure;
+	if (!lpm_empty) {
+		ret = ethnl_put_bitset32(skb, ETHTOOL_A_LINKMODES_PEER,
+					 __ETHTOOL_LINK_MODE_MASK_NBITS,
+					 lp_adv, NULL, link_mode_names,
+					 flags | ETHNL_BITSET_LIST);
+		if (ret < 0)
+			goto nla_put_failure;
+	}
+
+	if (nla_put_u32(skb, ETHTOOL_A_LINKMODES_SPEED, lsettings->speed) ||
+	    nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, nest);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, nest);
+	return -EMSGSIZE;
+}
+
+/* fill_reply() handler */
+static int settings_fill(struct sk_buff *skb,
+			 const struct ethnl_req_info *req_info)
+{
+	const struct settings_data *data =
+		container_of(req_info, struct settings_data, reqinfo_base);
+	u32 info_mask = data->repdata_base.info_mask;
+	bool compact = req_info->global_flags & ETHTOOL_RF_COMPACT;
+	int ret;
+
+	if (info_mask & ETHTOOL_IM_SETTINGS_LINKINFO) {
+		ret = settings_fill_linkinfo(skb, data->lsettings);
+		if (ret < 0)
+			return ret;
+	}
+	if (info_mask & ETHTOOL_IM_SETTINGS_LINKMODES) {
+		ret = settings_fill_linkmodes(skb, &data->ksettings,
+					      data->lpm_empty, compact);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+const struct get_request_ops settings_request_ops = {
+	.request_cmd		= ETHTOOL_MSG_SETTINGS_GET,
+	.reply_cmd		= ETHTOOL_MSG_SETTINGS_GET_REPLY,
+	.hdr_attr		= ETHTOOL_A_SETTINGS_HEADER,
+	.max_attr		= ETHTOOL_A_SETTINGS_MAX,
+	.data_size		= sizeof(struct settings_data),
+	.repdata_offset		= offsetof(struct settings_data, repdata_base),
+	.request_policy		= settings_get_policy,
+	.default_infomask	= ETHTOOL_IM_SETTINGS_ALL,
+	.all_reqflags		= ETHTOOL_RF_SETTINGS_ALL,
+
+	.prepare_data		= settings_prepare,
+	.reply_size		= settings_size,
+	.fill_reply		= settings_fill,
+};
-- 
2.22.0


  parent reply	other threads:[~2019-07-02 11:50 UTC|newest]

Thread overview: 77+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-02 11:49 [PATCH net-next v6 00/15] ethtool netlink interface, part 1 Michal Kubecek
2019-07-02 11:49 ` [PATCH net-next v6 01/15] rtnetlink: provide permanent hardware address in RTM_NEWLINK Michal Kubecek
2019-07-02 11:57   ` Jiri Pirko
2019-07-02 14:55   ` Stephen Hemminger
2019-07-02 16:35     ` Michal Kubecek
2019-07-02 11:49 ` [PATCH net-next v6 02/15] netlink: rename nl80211_validate_nested() to nla_validate_nested() Michal Kubecek
2019-07-02 12:03   ` Jiri Pirko
2019-07-02 12:15   ` Johannes Berg
2019-07-02 12:15   ` Johannes Berg
2019-07-02 11:49 ` [PATCH net-next v6 03/15] ethtool: move to its own directory Michal Kubecek
2019-07-02 11:49 ` [PATCH net-next v6 04/15] ethtool: introduce ethtool netlink interface Michal Kubecek
2019-07-02 12:25   ` Jiri Pirko
2019-07-02 14:52     ` Michal Kubecek
2019-07-03  8:41       ` Jiri Pirko
2019-07-08 17:27         ` Michal Kubecek
2019-07-08 18:12           ` Johannes Berg
2019-07-08 19:26           ` Jiri Pirko
2019-07-08 19:28             ` Jiri Pirko
2019-07-08 20:22             ` Michal Kubecek
2019-07-09 13:42               ` Jiri Pirko
2019-07-10 12:12                 ` Michal Kubecek
2019-07-03  1:29   ` Jakub Kicinski
2019-07-03  6:35     ` Michal Kubecek
2019-07-02 11:50 ` [PATCH net-next v6 05/15] ethtool: helper functions for " Michal Kubecek
2019-07-02 13:05   ` Jiri Pirko
2019-07-02 16:34     ` Michal Kubecek
2019-07-03  1:28       ` Jakub Kicinski
2019-07-03 10:04       ` Jiri Pirko
2019-07-03 11:13         ` Michal Kubecek
2019-07-08 12:22         ` Michal Kubecek
2019-07-08 14:40           ` Jiri Pirko
2019-07-03  1:37   ` Jakub Kicinski
2019-07-03  7:23     ` Michal Kubecek
2019-07-02 11:50 ` [PATCH net-next v6 06/15] ethtool: netlink bitset handling Michal Kubecek
2019-07-03 11:49   ` Jiri Pirko
2019-07-03 13:44     ` Johannes Berg
2019-07-03 14:37       ` Jiri Pirko
2019-07-04 12:07         ` Johannes Berg
2019-07-03 18:18     ` Michal Kubecek
2019-07-04  8:04       ` Jiri Pirko
2019-07-04 11:52         ` Michal Kubecek
2019-07-04 12:03           ` Johannes Berg
2019-07-04 12:17             ` Michal Kubecek
2019-07-04 12:21               ` Johannes Berg
2019-07-04 12:53                 ` Michal Kubecek
2019-07-04 13:10                   ` Johannes Berg
2019-07-04 14:31                     ` Andrew Lunn
2019-07-09 14:18           ` Jiri Pirko
2019-07-10 12:38             ` Michal Kubecek
2019-07-10 12:59               ` Jiri Pirko
2019-07-10 14:37                 ` Michal Kubecek
2019-07-02 11:50 ` [PATCH net-next v6 07/15] ethtool: support for netlink notifications Michal Kubecek
2019-07-03 13:33   ` Jiri Pirko
2019-07-03 14:16     ` Michal Kubecek
2019-07-04  8:06       ` Jiri Pirko
2019-07-03 13:39   ` Johannes Berg
2019-07-03 14:18     ` Michal Kubecek
2019-07-02 11:50 ` [PATCH net-next v6 08/15] ethtool: move string arrays into common file Michal Kubecek
2019-07-03 13:44   ` Jiri Pirko
2019-07-03 14:37     ` Michal Kubecek
2019-07-04  8:09       ` Jiri Pirko
2019-07-02 11:50 ` [PATCH net-next v6 09/15] ethtool: generic handlers for GET requests Michal Kubecek
2019-07-03 14:25   ` Jiri Pirko
2019-07-03 17:53     ` Michal Kubecek
2019-07-04  8:45       ` Jiri Pirko
2019-07-04  8:49   ` Jiri Pirko
2019-07-04  9:28     ` Michal Kubecek
2019-07-02 11:50 ` [PATCH net-next v6 10/15] ethtool: provide string sets with STRSET_GET request Michal Kubecek
2019-07-04  8:17   ` Jiri Pirko
2019-07-02 11:50 ` [PATCH net-next v6 11/15] ethtool: provide link mode names as a string set Michal Kubecek
2019-07-03  2:04   ` Jakub Kicinski
2019-07-03  2:11     ` Jakub Kicinski
2019-07-03  7:38       ` Michal Kubecek
2019-07-02 11:50 ` Michal Kubecek [this message]
2019-07-02 11:50 ` [PATCH net-next v6 13/15] ethtool: add standard notification handler Michal Kubecek
2019-07-02 11:50 ` [PATCH net-next v6 14/15] ethtool: set link settings and link modes with SETTINGS_SET request Michal Kubecek
2019-07-02 11:50 ` [PATCH net-next v6 15/15] ethtool: provide link state in SETTINGS_GET request Michal Kubecek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6a734345f85fb2010015303ee14941cdeea9559a.1562067622.git.mkubecek@suse.cz \
    --to=mkubecek@suse.cz \
    --cc=andrew@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=f.fainelli@gmail.com \
    --cc=jakub.kicinski@netronome.com \
    --cc=jiri@resnulli.us \
    --cc=johannes@sipsolutions.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linville@tuxdriver.com \
    --cc=netdev@vger.kernel.org \
    --cc=stephen@networkplumber.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).