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

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