All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michal Kubecek <mkubecek@suse.cz>
To: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, Jiri Pirko <jiri@resnulli.us>,
	David Miller <davem@davemloft.net>,
	Florian Fainelli <f.fainelli@gmail.com>,
	Roopa Prabhu <roopa@cumulusnetworks.com>,
	Jakub Kicinski <kubakici@wp.pl>,
	"John W. Linville" <linville@tuxdriver.com>
Subject: [RFC PATCH net-next v2 13/17] ethtool: implement SET_SETTINGS message
Date: Mon, 30 Jul 2018 14:53:47 +0200 (CEST)	[thread overview]
Message-ID: <766faf06bab06022ab53820e2d6bc7b213fcd989.1532953989.git.mkubecek@suse.cz> (raw)
In-Reply-To: <cover.1532953989.git.mkubecek@suse.cz>

Sets the information provided by ETHTOOL_SLINKSETTINGS, ETHTOOL_SWOL and
ETHTOOL_SMSGLVL. Unlike with ioctl(), userspace can send only some
attributes so that we only need to call ethtool_ops callbacks which we
really need (and the "set" callback is only called when we actually changed
some setting).

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  43 +-
 net/ethtool/netlink.c                        |   6 +
 net/ethtool/settings.c                       | 495 +++++++++++++++++++
 3 files changed, 539 insertions(+), 5 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 3993652f81a9..c7fe4f518972 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -124,7 +124,7 @@ List of message types
     ETHNL_CMD_GET_DRVINFO
     ETHNL_CMD_SET_DRVINFO		response only
     ETHNL_CMD_GET_SETTINGS
-    ETHNL_CMD_SET_SETTINGS		response only (for now)
+    ETHNL_CMD_SET_SETTINGS
 
 All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
 to indicate the type.
@@ -267,6 +267,39 @@ netlink or ioctl ethtool interface; feature notifications are also sent
 whenever netdev_update_features() or netdev_change_features() is called.
 
 
+SET_SETTINGS
+------------
+
+SET_SETTINGS request allows setting some of the data reported by GET_SETTINGS.
+Request flags, info_mask and index are ignored. These attributes are allowed
+to be passed with SET_SETTINGS request:
+
+    ETHA_SETTINGS_DEV		(nested)	device identification
+    ETHA_SETTINGS_SPEED         (u32)           link speed (Mb/s)
+    ETHA_SETTINGS_DUPLEX        (u8)            duplex mode
+    ETHA_SETTINGS_PORT          (u8)            physical port
+    ETHA_SETTINGS_PHYADDR       (u8)            MDIO address of phy
+    ETHA_SETTINGS_AUTONEG       (u8)            autoneotiation status
+    ETHA_SETTINGS_TP_MDIX_CTRL  (u8)            MDI(-X) control
+    ETHA_SETTINGS_WOL_MODES     (bitfield32)    wake-on-lan modes
+    ETHA_SETTINGS_SOPASS        (binary)        SecureOn(tm) password
+    ETHA_SETTINGS_MSGLVL        (bitfield32)    debug level
+    ETHA_SETTINGS_LINK_MODES    (bitset)        device link modes
+
+For both bitfield32 types, value and selector work the usual way, i.e. bits
+set in selector are set to corresponding bits from value and the rest is
+preserved. In a similar fashion, ETHA_SETTINGS_LINK_MODES allows setting
+advertised link modes.
+
+If autonegotiation is on (either set now or kept from before), advertised
+modes are not changed (no ETHA_SETTINGS_LINK_MODES attribute) and at least one
+of speed and duplex is specified, kernel adjusts advertised modes to all
+supported modes matching speed, duplex or both (whatever is specified). This
+autoselection is done on ethtool side with ioctl interface, netlink interface
+is supposed to allow requesting changes without knowing what exactly kernel
+supports.
+
+
 Request translation
 -------------------
 
@@ -277,13 +310,13 @@ have their netlink replacement yet.
 ioctl command			netlink command
 ---------------------------------------------------------------------
 ETHTOOL_GSET			ETHNL_CMD_GET_SETTINGS
-ETHTOOL_SSET			n/a
+ETHTOOL_SSET			ETHNL_CMD_SET_SETTINGS
 ETHTOOL_GDRVINFO		ETHNL_CMD_GET_DRVINFO
 ETHTOOL_GREGS			n/a
 ETHTOOL_GWOL			ETHNL_CMD_GET_SETTINGS
-ETHTOOL_SWOL			n/a
+ETHTOOL_SWOL			ETHNL_CMD_SET_SETTINGS
 ETHTOOL_GMSGLVL			ETHNL_CMD_GET_SETTINGS
-ETHTOOL_SMSGLVL			n/a
+ETHTOOL_SMSGLVL			ETHNL_CMD_SET_SETTINGS
 ETHTOOL_NWAY_RST		n/a
 ETHTOOL_GLINK			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_GEEPROM			n/a
@@ -351,7 +384,7 @@ ETHTOOL_STUNABLE		n/a
 ETHTOOL_GPHYSTATS		n/a
 ETHTOOL_PERQUEUE		n/a
 ETHTOOL_GLINKSETTINGS		ETHNL_CMD_GET_SETTINGS
-ETHTOOL_SLINKSETTINGS		n/a
+ETHTOOL_SLINKSETTINGS		ETHNL_CMD_SET_SETTINGS
 ETHTOOL_PHY_GTUNABLE		n/a
 ETHTOOL_PHY_STUNABLE		n/a
 ETHTOOL_GFECPARAM		n/a
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 0e2158092a44..6e183caca01f 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -730,6 +730,7 @@ static struct notifier_block ethnl_netdev_notifier = {
 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_strset_start(struct netlink_callback *cb);
 int ethnl_drvinfo_start(struct netlink_callback *cb);
@@ -759,6 +760,11 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.dumpit	= ethnl_dumpit,
 		.done	= ethnl_settings_done,
 	},
+	{
+		.cmd	= ETHNL_CMD_SET_SETTINGS,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= ethnl_set_settings,
+	},
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 739bd006c924..678199e621a2 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -88,6 +88,222 @@ struct settings_reqinfo {
 	bool				have_rtnl;
 };
 
+struct link_mode_info {
+	int				speed;
+	u8				duplex;
+};
+
+static const struct link_mode_info link_mode_params[] = {
+	[ETHTOOL_LINK_MODE_10baseT_Half_BIT]		= {
+		.speed	= 10,
+		.duplex	= DUPLEX_HALF,
+	},
+	[ETHTOOL_LINK_MODE_10baseT_Full_BIT]		= {
+		.speed	= 10,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100baseT_Half_BIT]		= {
+		.speed	= 100,
+		.duplex	= DUPLEX_HALF,
+	},
+	[ETHTOOL_LINK_MODE_100baseT_Full_BIT]		= {
+		.speed	= 100,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_1000baseT_Half_BIT]		= {
+		.speed	= 1000,
+		.duplex	= DUPLEX_HALF,
+	},
+	[ETHTOOL_LINK_MODE_1000baseT_Full_BIT]		= {
+		.speed	= 1000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_Autoneg_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_TP_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_AUI_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_MII_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_FIBRE_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_BNC_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_10000baseT_Full_BIT]		= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_Pause_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_Asym_Pause_BIT]		= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_2500baseX_Full_BIT]		= {
+		.speed	= 2500,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_Backplane_BIT]		= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_1000baseKX_Full_BIT]		= {
+		.speed	= 1000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseKR_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseR_FEC_BIT]		= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT]	= {
+		.speed	= 20000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT]	= {
+		.speed	= 20000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT]	= {
+		.speed	= 40000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT]	= {
+		.speed	= 40000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT]	= {
+		.speed	= 40000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT]	= {
+		.speed	= 40000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT]	= {
+		.speed	= 56000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT]	= {
+		.speed	= 56000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT]	= {
+		.speed	= 56000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT]	= {
+		.speed	= 56000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_25000baseCR_Full_BIT]	= {
+		.speed	= 25000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_25000baseKR_Full_BIT]	= {
+		.speed	= 25000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_25000baseSR_Full_BIT]	= {
+		.speed	= 25000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT]	= {
+		.speed	= 50000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT]	= {
+		.speed	= 50000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT]	= {
+		.speed	= 100000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT]	= {
+		.speed	= 100000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT]	= {
+		.speed	= 100000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT]	= {
+		.speed	= 100000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT]	= {
+		.speed	= 50000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_1000baseX_Full_BIT]		= {
+		.speed	= 1000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseCR_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseSR_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseLR_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseER_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_2500baseT_Full_BIT]		= {
+		.speed	= 2500,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_5000baseT_Full_BIT]		= {
+		.speed	= 5000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_FEC_NONE_BIT]		= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_FEC_RS_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_FEC_BASER_BIT]		= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+};
+
 /* We want to allow ~0 as selector for backward compatibility (to just set
  * given set of modes, whatever kernel supports) so that we allow all bits
  * on validation and do our own sanity check later.
@@ -606,3 +822,282 @@ void ethnl_settings_notify(struct netdev_notifier_ethtool_info *info)
 err_skb:
 	nlmsg_free(skb);
 }
+
+/* SET_SETTINGS */
+
+static int ethnl_set_link_ksettings(struct genl_info *info,
+				    struct net_device *dev,
+				    struct ethtool_link_ksettings *ksettings)
+{
+	int ret = dev->ethtool_ops->set_link_ksettings(dev, ksettings);
+
+	if (ret < 0)
+		ETHNL_SET_ERRMSG(info, "link settings update failed");
+	return ret;
+}
+
+static int ethnl_set_legacy_settings(struct genl_info *info,
+				     struct net_device *dev,
+				     struct ethtool_cmd *cmd)
+{
+	int ret;
+
+	if (!dev->ethtool_ops->set_settings) {
+		/* we already tried ->set_link_ksettings */
+		ETHNL_SET_ERRMSG(info, "link settings update unsupported");
+		return -EOPNOTSUPP;
+	}
+	ret = dev->ethtool_ops->set_settings(dev, cmd);
+	if (ret < 0)
+		ETHNL_SET_ERRMSG(info, "link settings update failed");
+
+	return ret;
+}
+
+static int ethnl_set_wol(struct genl_info *info, struct net_device *dev,
+			 struct ethtool_wolinfo *wolinfo)
+{
+	int ret = dev->ethtool_ops->set_wol(dev, wolinfo);
+
+	if (ret < 0)
+		ETHNL_SET_ERRMSG(info, "wol info update failed");
+	return ret;
+}
+
+/* Set advertised link modes to all supported modes matching requested speed
+ * and duplex values. Called when autonegotiation is on, speed or duplex is
+ * requested but no link mode change. This is done in userspace with ioctl()
+ * interface, move it into kernel for netlink.
+ * Returns true if advertised modes bitmap was modified.
+ */
+static bool auto_link_modes(unsigned long *supported,
+			    unsigned long *advertising, unsigned int nbits,
+			    struct nlattr *speed_attr,
+			    struct nlattr *duplex_attr)
+{
+	u8 duplex = duplex_attr ? nla_get_u8(duplex_attr) : DUPLEX_UNKNOWN;
+	u32 speed = speed_attr ? nla_get_u32(speed_attr) : SPEED_UNKNOWN;
+	DECLARE_BITMAP(old_adv, nbits);
+	unsigned int i;
+
+	bitmap_copy(old_adv, advertising, nbits);
+
+	for (i = 0; i < nbits; i++) {
+		const struct link_mode_info *info = &link_mode_params[i];
+
+		if (info->speed == SPEED_UNKNOWN)
+			continue;
+		if (test_bit(i, supported) &&
+		    (!speed_attr || info->speed == speed) &&
+		    (!duplex_attr || info->duplex == duplex))
+			set_bit(i, advertising);
+		else
+			clear_bit(i, advertising);
+	}
+
+	return !bitmap_equal(old_adv, advertising, nbits);
+}
+
+/* Update device settings using ->set_link_ksettings() callback */
+static int ethnl_update_ksettings(struct genl_info *info, struct nlattr **tb,
+				  struct net_device *dev)
+{
+	struct ethtool_link_ksettings ksettings = {};
+	struct ethtool_link_settings *lsettings;
+	bool mod = false;
+	int ret;
+
+	ret = ethnl_get_link_ksettings(info, dev, &ksettings);
+	if (ret < 0)
+		return ret;
+	lsettings = &ksettings.base;
+
+	mod = false;
+	if (ethnl_update_u32(&lsettings->speed, tb[ETHA_SETTINGS_SPEED]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->duplex, tb[ETHA_SETTINGS_DUPLEX]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->port, tb[ETHA_SETTINGS_PORT]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->phy_address, tb[ETHA_SETTINGS_PHYADDR]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->autoneg, tb[ETHA_SETTINGS_AUTONEG]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl,
+			    tb[ETHA_SETTINGS_TP_MDIX_CTRL]))
+		mod = true;
+	if (ethnl_update_bitset(ksettings.link_modes.advertising, NULL,
+				__ETHTOOL_LINK_MODE_MASK_NBITS,
+				tb[ETHA_SETTINGS_LINK_MODES],
+				&ret, link_mode_names, info))
+		mod = true;
+	if (ret < 0)
+		return ret;
+
+	if (!tb[ETHA_SETTINGS_LINK_MODES] && lsettings->autoneg &&
+	    (tb[ETHA_SETTINGS_SPEED] || tb[ETHA_SETTINGS_DUPLEX])) {
+		if (auto_link_modes(ksettings.link_modes.supported,
+				    ksettings.link_modes.advertising,
+				    __ETHTOOL_LINK_MODE_MASK_NBITS,
+				    tb[ETHA_SETTINGS_SPEED],
+				    tb[ETHA_SETTINGS_DUPLEX]))
+			mod = true;
+	}
+
+	if (mod) {
+		ret = ethnl_set_link_ksettings(info, dev, &ksettings);
+		if (ret < 0)
+			return ret;
+	}
+
+	return mod ? 1 : 0;
+}
+
+/* Update legacy settings using ->set_settings() callback. */
+static int ethnl_update_lsettings(struct genl_info *info, struct nlattr **tb,
+				  struct net_device *dev)
+{
+	struct ethtool_cmd cmd = {};
+	const unsigned int nbits = sizeof(cmd.advertising) * BITS_PER_BYTE;
+	DECLARE_BITMAP(advertising, nbits);
+	DECLARE_BITMAP(supported, nbits);
+	bool mod = false;
+	u32 speed;
+	int ret;
+
+	ret = ethnl_get_legacy_settings(info, dev, &cmd);
+	if (ret < 0)
+		return ret;
+	bitmap_from_arr32(supported, &cmd.supported, nbits);
+	bitmap_from_arr32(advertising, &cmd.advertising, nbits);
+
+	mod = false;
+	speed = ethtool_cmd_speed(&cmd);
+	if (ethnl_update_u32(&speed, tb[ETHA_SETTINGS_SPEED])) {
+		ethtool_cmd_speed_set(&cmd, speed);
+		mod = true;
+	}
+	if (ethnl_update_u8(&cmd.duplex, tb[ETHA_SETTINGS_DUPLEX]))
+		mod = true;
+	if (ethnl_update_u8(&cmd.port, tb[ETHA_SETTINGS_PORT]))
+		mod = true;
+	if (ethnl_update_u8(&cmd.phy_address, tb[ETHA_SETTINGS_PHYADDR]))
+		mod = true;
+	if (ethnl_update_u8(&cmd.autoneg, tb[ETHA_SETTINGS_AUTONEG]))
+		mod = true;
+	if (ethnl_update_u8(&cmd.eth_tp_mdix_ctrl,
+			    tb[ETHA_SETTINGS_TP_MDIX_CTRL]))
+		mod = true;
+	if (ethnl_update_bitset(advertising, NULL, nbits,
+				tb[ETHA_SETTINGS_LINK_MODES],
+				&ret, link_mode_names, info)) {
+		bitmap_to_arr32(&cmd.advertising, advertising, nbits);
+		mod = true;
+	}
+	if (ret < 0)
+		return ret;
+
+	if (!tb[ETHA_SETTINGS_LINK_MODES] && cmd.autoneg &&
+	    (tb[ETHA_SETTINGS_SPEED] || tb[ETHA_SETTINGS_DUPLEX])) {
+		if (auto_link_modes(supported, advertising, nbits,
+				    tb[ETHA_SETTINGS_SPEED],
+				    tb[ETHA_SETTINGS_DUPLEX])) {
+			bitmap_to_arr32(&cmd.advertising, advertising, nbits);
+			mod = true;
+		}
+	}
+
+	if (mod) {
+		ret = ethnl_set_legacy_settings(info, dev, &cmd);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
+	struct ethtool_wolinfo wolinfo = {};
+	struct net_device *dev;
+	u32 req_mask = 0;
+	bool mod;
+	int ret;
+
+	ret = genlmsg_parse(info->nlhdr, &ethtool_genl_family, tb,
+			    ETHA_SETTINGS_MAX, settings_policy, info->extack);
+	if (ret < 0)
+		return ret;
+	dev = ethnl_dev_get(info, tb[ETHA_SETTINGS_DEV]);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+	rtnl_lock();
+
+	/* read only attributes */
+	ret = -EINVAL;
+	if (tb[ETHA_SETTINGS_MDIO_SUPPORT] || tb[ETHA_SETTINGS_TP_MDIX] ||
+	    tb[ETHA_SETTINGS_TRANSCEIVER] || tb[ETHA_SETTINGS_PEER_MODES] ||
+	    tb[ETHA_SETTINGS_LINK]) {
+		ETHNL_SET_ERRMSG(info, "attempt to set a read only attribute");
+		goto out_unlock;
+	}
+
+	if (tb[ETHA_SETTINGS_SPEED] || tb[ETHA_SETTINGS_DUPLEX] ||
+	    tb[ETHA_SETTINGS_PORT] || tb[ETHA_SETTINGS_PHYADDR] ||
+	    tb[ETHA_SETTINGS_AUTONEG] || tb[ETHA_SETTINGS_TP_MDIX_CTRL] ||
+	    tb[ETHA_SETTINGS_LINK_MODES]) {
+		if (dev->ethtool_ops->get_link_ksettings)
+			ret = ethnl_update_ksettings(info, tb, dev);
+		else
+			ret = ethnl_update_lsettings(info, tb, dev);
+		if (ret < 0)
+			goto out_unlock;
+		req_mask |= ETH_SETTINGS_IM_LINKINFO |
+			    ETH_SETTINGS_IM_LINKMODES;
+	}
+	if (tb[ETHA_SETTINGS_WOL_MODES] || tb[ETHA_SETTINGS_SOPASS]) {
+		ret = ethnl_get_wol(info, dev, &wolinfo);
+		if (ret < 0)
+			goto out_unlock;
+
+		mod = false;
+		if (ethnl_update_bitfield32(&wolinfo.wolopts,
+					    tb[ETHA_SETTINGS_WOL_MODES]))
+			mod = true;
+		if (ethnl_update_binary(wolinfo.sopass, SOPASS_MAX,
+					tb[ETHA_SETTINGS_SOPASS]))
+			mod = true;
+		if (mod) {
+			ret = ethnl_set_wol(info, dev, &wolinfo);
+			if (ret < 0)
+				goto out_unlock;
+			req_mask |= ETH_SETTINGS_IM_WOLINFO;
+		}
+	}
+	if (tb[ETHA_SETTINGS_MSGLVL]) {
+		u32 msglvl;
+
+		ret = -EOPNOTSUPP;
+		if (!dev->ethtool_ops->get_msglevel ||
+		    !dev->ethtool_ops->set_msglevel) {
+			ETHNL_SET_ERRMSG(info,
+					 "device does not provide msglvl access");
+			goto out_unlock;
+		}
+		msglvl = dev->ethtool_ops->get_msglevel(dev);
+		if (ethnl_update_bitfield32(&msglvl,
+					    tb[ETHA_SETTINGS_MSGLVL])) {
+			dev->ethtool_ops->set_msglevel(dev, msglvl);
+			req_mask |= ETH_SETTINGS_IM_MSGLEVEL;
+		}
+	}
+	ret = 0;
+
+out_unlock:
+	if (req_mask)
+		ethnl_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS, req_mask);
+	rtnl_unlock();
+	dev_put(dev);
+	return ret;
+}
-- 
2.18.0


  parent reply	other threads:[~2018-07-30 12:53 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-30 12:52 [RFC PATCH net-next v2 00/17] ethtool netlink interface (WiP) Michal Kubecek
2018-07-30 12:52 ` [RFC PATCH net-next v2 01/17] netlink: introduce nla_put_bitfield32() Michal Kubecek
2018-07-30 12:52 ` [RFC PATCH net-next v2 02/17] ethtool: move to its own directory Michal Kubecek
2018-07-30 12:52 ` [RFC PATCH net-next v2 03/17] ethtool: introduce ethtool netlink interface Michal Kubecek
2018-07-30 12:53 ` [RFC PATCH net-next v2 04/17] ethtool: helper functions for " Michal Kubecek
2018-07-30 12:53 ` [RFC PATCH net-next v2 05/17] ethtool: netlink bitset handling Michal Kubecek
2018-07-30 12:53 ` [RFC PATCH net-next v2 06/17] ethtool: support for netlink notifications Michal Kubecek
2018-07-30 13:16   ` Jiri Pirko
2018-07-30 17:01     ` Michal Kubecek
2018-07-31  6:46       ` Jiri Pirko
2018-07-30 12:53 ` [RFC PATCH net-next v2 07/17] ethtool: implement EVENT notifications Michal Kubecek
2018-07-30 12:53 ` [RFC PATCH net-next v2 08/17] ethtool: implement GET_STRSET message Michal Kubecek
2018-07-30 12:53 ` [RFC PATCH net-next v2 09/17] ethtool: implement GET_DRVINFO message Michal Kubecek
2018-07-30 13:21   ` Jiri Pirko
2018-07-30 14:37     ` Michal Kubecek
2018-07-30 14:28   ` Andrew Lunn
2018-07-30 14:46     ` Michal Kubecek
2018-07-30 15:48       ` Andrew Lunn
2018-07-30 16:47         ` Michal Kubecek
2018-07-31  0:56   ` Jakub Kicinski
2018-07-30 12:53 ` [RFC PATCH net-next v2 10/17] ethtool: implement GET_SETTINGS message Michal Kubecek
2018-07-30 18:54   ` Andrew Lunn
2018-08-21  9:32     ` Michal Kubecek
2018-08-21 14:10       ` Andrew Lunn
2018-08-21 14:52         ` Michal Kubecek
2018-07-30 19:06   ` Andrew Lunn
2018-07-30 19:09   ` Andrew Lunn
2018-07-30 19:42     ` Michal Kubecek
2018-07-30 12:53 ` [RFC PATCH net-next v2 11/17] ethtool: implement GET_SETTINGS request for features Michal Kubecek
2018-07-30 12:53 ` [RFC PATCH net-next v2 12/17] ethtool: implement SET_SETTINGS notification Michal Kubecek
2018-07-30 12:53 ` Michal Kubecek [this message]
2018-07-30 12:53 ` [RFC PATCH net-next v2 14/17] ethtool: implement SET_SETTINGS request for features Michal Kubecek
2018-07-30 12:53 ` [RFC PATCH net-next v2 15/17] ethtool: implement GET_PARAMS message Michal Kubecek
2018-07-30 12:54 ` [RFC PATCH net-next v2 16/17] ethtool: implement SET_PARAMS notification Michal Kubecek
2018-07-30 12:54 ` [RFC PATCH net-next v2 17/17] ethtool: implement SET_PARAMS message Michal Kubecek
2018-07-30 13:07 ` [RFC PATCH net-next v2 00/17] ethtool netlink interface (WiP) Jiri Pirko
2018-07-31  0:38   ` Jakub Kicinski

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=766faf06bab06022ab53820e2d6bc7b213fcd989.1532953989.git.mkubecek@suse.cz \
    --to=mkubecek@suse.cz \
    --cc=davem@davemloft.net \
    --cc=f.fainelli@gmail.com \
    --cc=jiri@resnulli.us \
    --cc=kubakici@wp.pl \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linville@tuxdriver.com \
    --cc=netdev@vger.kernel.org \
    --cc=roopa@cumulusnetworks.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.