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