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

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