Blame SOURCES/0026-ethtool-Improve-compatibility-between-netlink-and-io.patch

3259a4
From f16bc54fe82b9129d6852273d02e044b9cb28789 Mon Sep 17 00:00:00 2001
3259a4
From: Ido Schimmel <idosch@nvidia.com>
3259a4
Date: Mon, 9 Nov 2020 14:29:59 +0100
3259a4
Subject: [PATCH 26/26] ethtool: Improve compatibility between netlink and
3259a4
 ioctl interfaces
3259a4
3259a4
With the ioctl interface, when autoneg is enabled, but without
3259a4
specifying speed, duplex or link modes, the advertised link modes are
3259a4
set to the supported link modes by the ethtool user space utility.
3259a4
3259a4
This does not happen when using the netlink interface. Fix this
3259a4
incompatibility problem by having ethtool query the supported link modes
3259a4
from the kernel and advertise all the "real" ones when only "autoneg on"
3259a4
is specified.
3259a4
3259a4
Before:
3259a4
3259a4
Settings for eth0:
3259a4
	Supported ports: [ TP ]
3259a4
	Supported link modes:   10baseT/Half 10baseT/Full
3259a4
	                        100baseT/Half 100baseT/Full
3259a4
	                        1000baseT/Full
3259a4
	Supported pause frame use: No
3259a4
	Supports auto-negotiation: Yes
3259a4
	Supported FEC modes: Not reported
3259a4
	Advertised link modes:  100baseT/Half 100baseT/Full
3259a4
	Advertised pause frame use: No
3259a4
	Advertised auto-negotiation: Yes
3259a4
	Advertised FEC modes: Not reported
3259a4
	Speed: 1000Mb/s
3259a4
	Duplex: Full
3259a4
	Auto-negotiation: on
3259a4
	Port: Twisted Pair
3259a4
	PHYAD: 0
3259a4
	Transceiver: internal
3259a4
	MDI-X: off (auto)
3259a4
	Supports Wake-on: umbg
3259a4
	Wake-on: d
3259a4
        Current message level: 0x00000007 (7)
3259a4
                               drv probe link
3259a4
	Link detected: yes
3259a4
3259a4
After:
3259a4
3259a4
Settings for eth0:
3259a4
	Supported ports: [ TP ]
3259a4
	Supported link modes:   10baseT/Half 10baseT/Full
3259a4
	                        100baseT/Half 100baseT/Full
3259a4
	                        1000baseT/Full
3259a4
	Supported pause frame use: No
3259a4
	Supports auto-negotiation: Yes
3259a4
	Supported FEC modes: Not reported
3259a4
	Advertised link modes:  10baseT/Half 10baseT/Full
3259a4
	                        100baseT/Half 100baseT/Full
3259a4
	                        1000baseT/Full
3259a4
	Advertised pause frame use: No
3259a4
	Advertised auto-negotiation: Yes
3259a4
	Advertised FEC modes: Not reported
3259a4
	Speed: 1000Mb/s
3259a4
	Duplex: Full
3259a4
	Auto-negotiation: on
3259a4
	Port: Twisted Pair
3259a4
	PHYAD: 0
3259a4
	Transceiver: internal
3259a4
	MDI-X: on (auto)
3259a4
	Supports Wake-on: umbg
3259a4
	Wake-on: d
3259a4
        Current message level: 0x00000007 (7)
3259a4
                               drv probe link
3259a4
	Link detected: yes
3259a4
3259a4
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
3259a4
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
3259a4
(cherry picked from commit 124a3c06d1c34b125d84a9eb312fddd365bb7bf6)
3259a4
---
3259a4
 netlink/settings.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++
3259a4
 1 file changed, 92 insertions(+)
3259a4
3259a4
diff --git a/netlink/settings.c b/netlink/settings.c
3259a4
index fac192e2fbb7..01c1d38d323f 100644
3259a4
--- a/netlink/settings.c
3259a4
+++ b/netlink/settings.c
3259a4
@@ -1113,6 +1113,93 @@ static const struct param_parser sset_params[] = {
3259a4
  */
3259a4
 #define SSET_MAX_MSGS 4
3259a4
 
3259a4
+static int linkmodes_reply_advert_all_cb(const struct nlmsghdr *nlhdr,
3259a4
+					 void *data)
3259a4
+{
3259a4
+	const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
3259a4
+	DECLARE_ATTR_TB_INFO(tb);
3259a4
+	struct nl_msg_buff *req_msgbuff = data;
3259a4
+	const struct nlattr *ours_attr;
3259a4
+	struct nlattr *req_bitset;
3259a4
+	uint32_t *supported_modes;
3259a4
+	unsigned int modes_count;
3259a4
+	unsigned int i;
3259a4
+	int ret;
3259a4
+
3259a4
+	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
3259a4
+	if (ret < 0)
3259a4
+		return MNL_CB_ERROR;
3259a4
+	ours_attr = tb[ETHTOOL_A_LINKMODES_OURS];
3259a4
+	if (!ours_attr)
3259a4
+		return MNL_CB_ERROR;
3259a4
+	modes_count = bitset_get_count(tb[ETHTOOL_A_LINKMODES_OURS], &ret;;
3259a4
+	if (ret < 0)
3259a4
+		return MNL_CB_ERROR;
3259a4
+	supported_modes = get_compact_bitset_mask(tb[ETHTOOL_A_LINKMODES_OURS]);
3259a4
+	if (!supported_modes)
3259a4
+		return MNL_CB_ERROR;
3259a4
+
3259a4
+	/* keep only "real" link modes */
3259a4
+	for (i = 0; i < modes_count; i++)
3259a4
+		if (!lm_class_match(i, LM_CLASS_REAL))
3259a4
+			supported_modes[i / 32] &= ~((uint32_t)1 << (i % 32));
3259a4
+
3259a4
+	req_bitset = ethnla_nest_start(req_msgbuff, ETHTOOL_A_LINKMODES_OURS);
3259a4
+	if (!req_bitset)
3259a4
+		return MNL_CB_ERROR;
3259a4
+
3259a4
+	if (ethnla_put_u32(req_msgbuff, ETHTOOL_A_BITSET_SIZE, modes_count) ||
3259a4
+	    ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_VALUE,
3259a4
+		       DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
3259a4
+		       supported_modes) ||
3259a4
+	    ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_MASK,
3259a4
+		       DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
3259a4
+		       supported_modes)) {
3259a4
+		ethnla_nest_cancel(req_msgbuff, req_bitset);
3259a4
+		return MNL_CB_ERROR;
3259a4
+	}
3259a4
+
3259a4
+	ethnla_nest_end(req_msgbuff, req_bitset);
3259a4
+	return MNL_CB_OK;
3259a4
+}
3259a4
+
3259a4
+/* For compatibility reasons with ioctl-based ethtool, when "autoneg on" is
3259a4
+ * specified without "advertise", "speed" and "duplex", we need to query the
3259a4
+ * supported link modes from the kernel and advertise all the "real" ones.
3259a4
+ */
3259a4
+static int nl_sset_compat_linkmodes(struct nl_context *nlctx,
3259a4
+				    struct nl_msg_buff *msgbuff)
3259a4
+{
3259a4
+	const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
3259a4
+	DECLARE_ATTR_TB_INFO(tb);
3259a4
+	struct nl_socket *nlsk = nlctx->ethnl_socket;
3259a4
+	int ret;
3259a4
+
3259a4
+	ret = mnl_attr_parse(msgbuff->nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
3259a4
+	if (ret < 0)
3259a4
+		return ret;
3259a4
+	if (!tb[ETHTOOL_A_LINKMODES_AUTONEG] || tb[ETHTOOL_A_LINKMODES_OURS] ||
3259a4
+	    tb[ETHTOOL_A_LINKMODES_SPEED] || tb[ETHTOOL_A_LINKMODES_DUPLEX])
3259a4
+		return 0;
3259a4
+	if (!mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG]))
3259a4
+		return 0;
3259a4
+
3259a4
+	/* all conditions satisfied, create ETHTOOL_A_LINKMODES_OURS */
3259a4
+	if (netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_GET, false) ||
3259a4
+	    netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_SET, false))
3259a4
+		return -EOPNOTSUPP;
3259a4
+	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_LINKMODES_GET,
3259a4
+				      ETHTOOL_A_LINKMODES_HEADER,
3259a4
+				      ETHTOOL_FLAG_COMPACT_BITSETS);
3259a4
+	if (ret < 0)
3259a4
+		return ret;
3259a4
+	ret = nlsock_sendmsg(nlsk, NULL);
3259a4
+	if (ret < 0)
3259a4
+		return ret;
3259a4
+	return nlsock_process_reply(nlsk, linkmodes_reply_advert_all_cb,
3259a4
+				    msgbuff);
3259a4
+}
3259a4
+
3259a4
 int nl_sset(struct cmd_context *ctx)
3259a4
 {
3259a4
 	struct nl_msg_buff *msgbuffs[SSET_MAX_MSGS] = {};
3259a4
@@ -1134,6 +1221,11 @@ int nl_sset(struct cmd_context *ctx)
3259a4
 	for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
3259a4
 		struct nl_socket *nlsk = nlctx->ethnl_socket;
3259a4
 
3259a4
+		if (msgbuffs[i]->genlhdr->cmd == ETHTOOL_MSG_LINKMODES_SET) {
3259a4
+			ret = nl_sset_compat_linkmodes(nlctx, msgbuffs[i]);
3259a4
+			if (ret < 0)
3259a4
+				goto out_free;
3259a4
+		}
3259a4
 		ret = nlsock_sendmsg(nlsk, msgbuffs[i]);
3259a4
 		if (ret < 0)
3259a4
 			goto out_free;
3259a4
-- 
3259a4
2.26.2
3259a4