Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ethtool: support FEC settings over netlink

Add FEC API to netlink.

This is not a 1-to-1 conversion.

FEC settings already depend on link modes to tell user which
modes are supported. Take this further an use link modes for
manual configuration. Old struct ethtool_fecparam is still
used to talk to the drivers, so we need to translate back
and forth. We can revisit the internal API if number of FEC
encodings starts to grow.

Enforce only one active FEC bit (by using a bit position
rather than another mask).

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jakub Kicinski and committed by
David S. Miller
1e5d1f69 28110056

+339 -3
+60 -2
Documentation/networking/ethtool-netlink.rst
··· 208 208 ``ETHTOOL_MSG_CABLE_TEST_ACT`` action start cable test 209 209 ``ETHTOOL_MSG_CABLE_TEST_TDR_ACT`` action start raw TDR cable test 210 210 ``ETHTOOL_MSG_TUNNEL_INFO_GET`` get tunnel offload info 211 + ``ETHTOOL_MSG_FEC_GET`` get FEC settings 212 + ``ETHTOOL_MSG_FEC_SET`` set FEC settings 211 213 ===================================== ================================ 212 214 213 215 Kernel to userspace: ··· 244 242 ``ETHTOOL_MSG_CABLE_TEST_NTF`` Cable test results 245 243 ``ETHTOOL_MSG_CABLE_TEST_TDR_NTF`` Cable test TDR results 246 244 ``ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY`` tunnel offload info 245 + ``ETHTOOL_MSG_FEC_GET_REPLY`` FEC settings 246 + ``ETHTOOL_MSG_FEC_NTF`` FEC settings 247 247 ===================================== ================================= 248 248 249 249 ``GET`` requests are sent by userspace applications to retrieve device ··· 1284 1280 For UDP tunnel table empty ``ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES`` indicates that 1285 1281 the table contains static entries, hard-coded by the NIC. 1286 1282 1283 + FEC_GET 1284 + ======= 1285 + 1286 + Gets FEC configuration and state like ``ETHTOOL_GFECPARAM`` ioctl request. 1287 + 1288 + Request contents: 1289 + 1290 + ===================================== ====== ========================== 1291 + ``ETHTOOL_A_FEC_HEADER`` nested request header 1292 + ===================================== ====== ========================== 1293 + 1294 + Kernel response contents: 1295 + 1296 + ===================================== ====== ========================== 1297 + ``ETHTOOL_A_FEC_HEADER`` nested request header 1298 + ``ETHTOOL_A_FEC_MODES`` bitset configured modes 1299 + ``ETHTOOL_A_FEC_AUTO`` bool FEC mode auto selection 1300 + ``ETHTOOL_A_FEC_ACTIVE`` u32 index of active FEC mode 1301 + ===================================== ====== ========================== 1302 + 1303 + ``ETHTOOL_A_FEC_ACTIVE`` is the bit index of the FEC link mode currently 1304 + active on the interface. This attribute may not be present if device does 1305 + not support FEC. 1306 + 1307 + ``ETHTOOL_A_FEC_MODES`` and ``ETHTOOL_A_FEC_AUTO`` are only meaningful when 1308 + autonegotiation is disabled. If ``ETHTOOL_A_FEC_AUTO`` is non-zero driver will 1309 + select the FEC mode automatically based on the parameters of the SFP module. 1310 + This is equivalent to the ``ETHTOOL_FEC_AUTO`` bit of the ioctl interface. 1311 + ``ETHTOOL_A_FEC_MODES`` carry the current FEC configuration using link mode 1312 + bits (rather than old ``ETHTOOL_FEC_*`` bits). 1313 + 1314 + FEC_SET 1315 + ======= 1316 + 1317 + Sets FEC parameters like ``ETHTOOL_SFECPARAM`` ioctl request. 1318 + 1319 + Request contents: 1320 + 1321 + ===================================== ====== ========================== 1322 + ``ETHTOOL_A_FEC_HEADER`` nested request header 1323 + ``ETHTOOL_A_FEC_MODES`` bitset configured modes 1324 + ``ETHTOOL_A_FEC_AUTO`` bool FEC mode auto selection 1325 + ===================================== ====== ========================== 1326 + 1327 + ``FEC_SET`` is only meaningful when autonegotiation is disabled. Otherwise 1328 + FEC mode is selected as part of autonegotiation. 1329 + 1330 + ``ETHTOOL_A_FEC_MODES`` selects which FEC mode should be used. It's recommended 1331 + to set only one bit, if multiple bits are set driver may choose between them 1332 + in an implementation specific way. 1333 + 1334 + ``ETHTOOL_A_FEC_AUTO`` requests the driver to choose FEC mode based on SFP 1335 + module parameters. This does not mean autonegotiation. 1336 + 1287 1337 Request translation 1288 1338 =================== 1289 1339 ··· 1431 1373 ``ETHTOOL_MSG_LINKMODES_SET`` 1432 1374 ``ETHTOOL_PHY_GTUNABLE`` n/a 1433 1375 ``ETHTOOL_PHY_STUNABLE`` n/a 1434 - ``ETHTOOL_GFECPARAM`` n/a 1435 - ``ETHTOOL_SFECPARAM`` n/a 1376 + ``ETHTOOL_GFECPARAM`` ``ETHTOOL_MSG_FEC_GET`` 1377 + ``ETHTOOL_SFECPARAM`` ``ETHTOOL_MSG_FEC_SET`` 1436 1378 n/a ''ETHTOOL_MSG_CABLE_TEST_ACT'' 1437 1379 n/a ''ETHTOOL_MSG_CABLE_TEST_TDR_ACT'' 1438 1380 n/a ``ETHTOOL_MSG_TUNNEL_INFO_GET``
+17
include/uapi/linux/ethtool_netlink.h
··· 42 42 ETHTOOL_MSG_CABLE_TEST_ACT, 43 43 ETHTOOL_MSG_CABLE_TEST_TDR_ACT, 44 44 ETHTOOL_MSG_TUNNEL_INFO_GET, 45 + ETHTOOL_MSG_FEC_GET, 46 + ETHTOOL_MSG_FEC_SET, 45 47 46 48 /* add new constants above here */ 47 49 __ETHTOOL_MSG_USER_CNT, ··· 82 80 ETHTOOL_MSG_CABLE_TEST_NTF, 83 81 ETHTOOL_MSG_CABLE_TEST_TDR_NTF, 84 82 ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, 83 + ETHTOOL_MSG_FEC_GET_REPLY, 84 + ETHTOOL_MSG_FEC_NTF, 85 85 86 86 /* add new constants above here */ 87 87 __ETHTOOL_MSG_KERNEL_CNT, ··· 631 627 /* add new constants above here */ 632 628 __ETHTOOL_A_TUNNEL_INFO_CNT, 633 629 ETHTOOL_A_TUNNEL_INFO_MAX = (__ETHTOOL_A_TUNNEL_INFO_CNT - 1) 630 + }; 631 + 632 + /* FEC */ 633 + 634 + enum { 635 + ETHTOOL_A_FEC_UNSPEC, 636 + ETHTOOL_A_FEC_HEADER, /* nest - _A_HEADER_* */ 637 + ETHTOOL_A_FEC_MODES, /* bitset */ 638 + ETHTOOL_A_FEC_AUTO, /* u8 */ 639 + ETHTOOL_A_FEC_ACTIVE, /* u32 */ 640 + 641 + __ETHTOOL_A_FEC_CNT, 642 + ETHTOOL_A_FEC_MAX = (__ETHTOOL_A_FEC_CNT - 1) 634 643 }; 635 644 636 645 /* generic netlink info */
+1 -1
net/ethtool/Makefile
··· 7 7 ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ 8 8 linkstate.o debug.o wol.o features.o privflags.o rings.o \ 9 9 channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \ 10 - tunnels.o 10 + tunnels.o fec.o
+238
net/ethtool/fec.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include "netlink.h" 4 + #include "common.h" 5 + #include "bitset.h" 6 + 7 + struct fec_req_info { 8 + struct ethnl_req_info base; 9 + }; 10 + 11 + struct fec_reply_data { 12 + struct ethnl_reply_data base; 13 + __ETHTOOL_DECLARE_LINK_MODE_MASK(fec_link_modes); 14 + u32 active_fec; 15 + u8 fec_auto; 16 + }; 17 + 18 + #define FEC_REPDATA(__reply_base) \ 19 + container_of(__reply_base, struct fec_reply_data, base) 20 + 21 + #define ETHTOOL_FEC_MASK ((ETHTOOL_FEC_LLRS << 1) - 1) 22 + 23 + const struct nla_policy ethnl_fec_get_policy[ETHTOOL_A_FEC_HEADER + 1] = { 24 + [ETHTOOL_A_FEC_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 25 + }; 26 + 27 + static void 28 + ethtool_fec_to_link_modes(u32 fec, unsigned long *link_modes, u8 *fec_auto) 29 + { 30 + if (fec_auto) 31 + *fec_auto = !!(fec & ETHTOOL_FEC_AUTO); 32 + 33 + if (fec & ETHTOOL_FEC_OFF) 34 + __set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, link_modes); 35 + if (fec & ETHTOOL_FEC_RS) 36 + __set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, link_modes); 37 + if (fec & ETHTOOL_FEC_BASER) 38 + __set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, link_modes); 39 + if (fec & ETHTOOL_FEC_LLRS) 40 + __set_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, link_modes); 41 + } 42 + 43 + static int 44 + ethtool_link_modes_to_fecparam(struct ethtool_fecparam *fec, 45 + unsigned long *link_modes, u8 fec_auto) 46 + { 47 + memset(fec, 0, sizeof(*fec)); 48 + 49 + if (fec_auto) 50 + fec->fec |= ETHTOOL_FEC_AUTO; 51 + 52 + if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, link_modes)) 53 + fec->fec |= ETHTOOL_FEC_OFF; 54 + if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, link_modes)) 55 + fec->fec |= ETHTOOL_FEC_RS; 56 + if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, link_modes)) 57 + fec->fec |= ETHTOOL_FEC_BASER; 58 + if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, link_modes)) 59 + fec->fec |= ETHTOOL_FEC_LLRS; 60 + 61 + if (!bitmap_empty(link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) 62 + return -EINVAL; 63 + 64 + return 0; 65 + } 66 + 67 + static int fec_prepare_data(const struct ethnl_req_info *req_base, 68 + struct ethnl_reply_data *reply_base, 69 + struct genl_info *info) 70 + { 71 + __ETHTOOL_DECLARE_LINK_MODE_MASK(active_fec_modes) = {}; 72 + struct fec_reply_data *data = FEC_REPDATA(reply_base); 73 + struct net_device *dev = reply_base->dev; 74 + struct ethtool_fecparam fec = {}; 75 + int ret; 76 + 77 + if (!dev->ethtool_ops->get_fecparam) 78 + return -EOPNOTSUPP; 79 + ret = ethnl_ops_begin(dev); 80 + if (ret < 0) 81 + return ret; 82 + ret = dev->ethtool_ops->get_fecparam(dev, &fec); 83 + ethnl_ops_complete(dev); 84 + if (ret) 85 + return ret; 86 + 87 + WARN_ON_ONCE(fec.reserved); 88 + 89 + ethtool_fec_to_link_modes(fec.fec, data->fec_link_modes, 90 + &data->fec_auto); 91 + 92 + ethtool_fec_to_link_modes(fec.active_fec, active_fec_modes, NULL); 93 + data->active_fec = find_first_bit(active_fec_modes, 94 + __ETHTOOL_LINK_MODE_MASK_NBITS); 95 + /* Don't report attr if no FEC mode set. Note that 96 + * ethtool_fecparam_to_link_modes() ignores NONE and AUTO. 97 + */ 98 + if (data->active_fec == __ETHTOOL_LINK_MODE_MASK_NBITS) 99 + data->active_fec = 0; 100 + 101 + return 0; 102 + } 103 + 104 + static int fec_reply_size(const struct ethnl_req_info *req_base, 105 + const struct ethnl_reply_data *reply_base) 106 + { 107 + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 108 + const struct fec_reply_data *data = FEC_REPDATA(reply_base); 109 + int len = 0; 110 + int ret; 111 + 112 + ret = ethnl_bitset_size(data->fec_link_modes, NULL, 113 + __ETHTOOL_LINK_MODE_MASK_NBITS, 114 + link_mode_names, compact); 115 + if (ret < 0) 116 + return ret; 117 + len += ret; 118 + 119 + len += nla_total_size(sizeof(u8)) + /* _FEC_AUTO */ 120 + nla_total_size(sizeof(u32)); /* _FEC_ACTIVE */ 121 + 122 + return len; 123 + } 124 + 125 + static int fec_fill_reply(struct sk_buff *skb, 126 + const struct ethnl_req_info *req_base, 127 + const struct ethnl_reply_data *reply_base) 128 + { 129 + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 130 + const struct fec_reply_data *data = FEC_REPDATA(reply_base); 131 + int ret; 132 + 133 + ret = ethnl_put_bitset(skb, ETHTOOL_A_FEC_MODES, 134 + data->fec_link_modes, NULL, 135 + __ETHTOOL_LINK_MODE_MASK_NBITS, 136 + link_mode_names, compact); 137 + if (ret < 0) 138 + return ret; 139 + 140 + if (nla_put_u8(skb, ETHTOOL_A_FEC_AUTO, data->fec_auto) || 141 + (data->active_fec && 142 + nla_put_u32(skb, ETHTOOL_A_FEC_ACTIVE, data->active_fec))) 143 + return -EMSGSIZE; 144 + 145 + return 0; 146 + } 147 + 148 + const struct ethnl_request_ops ethnl_fec_request_ops = { 149 + .request_cmd = ETHTOOL_MSG_FEC_GET, 150 + .reply_cmd = ETHTOOL_MSG_FEC_GET_REPLY, 151 + .hdr_attr = ETHTOOL_A_FEC_HEADER, 152 + .req_info_size = sizeof(struct fec_req_info), 153 + .reply_data_size = sizeof(struct fec_reply_data), 154 + 155 + .prepare_data = fec_prepare_data, 156 + .reply_size = fec_reply_size, 157 + .fill_reply = fec_fill_reply, 158 + }; 159 + 160 + /* FEC_SET */ 161 + 162 + const struct nla_policy ethnl_fec_set_policy[ETHTOOL_A_FEC_AUTO + 1] = { 163 + [ETHTOOL_A_FEC_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 164 + [ETHTOOL_A_FEC_MODES] = { .type = NLA_NESTED }, 165 + [ETHTOOL_A_FEC_AUTO] = NLA_POLICY_MAX(NLA_U8, 1), 166 + }; 167 + 168 + int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info) 169 + { 170 + __ETHTOOL_DECLARE_LINK_MODE_MASK(fec_link_modes) = {}; 171 + struct ethnl_req_info req_info = {}; 172 + struct nlattr **tb = info->attrs; 173 + struct ethtool_fecparam fec = {}; 174 + const struct ethtool_ops *ops; 175 + struct net_device *dev; 176 + bool mod = false; 177 + u8 fec_auto; 178 + int ret; 179 + 180 + ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_FEC_HEADER], 181 + genl_info_net(info), info->extack, 182 + true); 183 + if (ret < 0) 184 + return ret; 185 + dev = req_info.dev; 186 + ops = dev->ethtool_ops; 187 + ret = -EOPNOTSUPP; 188 + if (!ops->get_fecparam || !ops->set_fecparam) 189 + goto out_dev; 190 + 191 + rtnl_lock(); 192 + ret = ethnl_ops_begin(dev); 193 + if (ret < 0) 194 + goto out_rtnl; 195 + ret = ops->get_fecparam(dev, &fec); 196 + if (ret < 0) 197 + goto out_ops; 198 + 199 + ethtool_fec_to_link_modes(fec.fec, fec_link_modes, &fec_auto); 200 + 201 + ret = ethnl_update_bitset(fec_link_modes, 202 + __ETHTOOL_LINK_MODE_MASK_NBITS, 203 + tb[ETHTOOL_A_FEC_MODES], 204 + link_mode_names, info->extack, &mod); 205 + if (ret < 0) 206 + goto out_ops; 207 + ethnl_update_u8(&fec_auto, tb[ETHTOOL_A_FEC_AUTO], &mod); 208 + 209 + ret = 0; 210 + if (!mod) 211 + goto out_ops; 212 + 213 + ret = ethtool_link_modes_to_fecparam(&fec, fec_link_modes, fec_auto); 214 + if (ret) { 215 + NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_FEC_MODES], 216 + "invalid FEC modes requested"); 217 + goto out_ops; 218 + } 219 + if (!fec.fec) { 220 + ret = -EINVAL; 221 + NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_FEC_MODES], 222 + "no FEC modes set"); 223 + goto out_ops; 224 + } 225 + 226 + ret = dev->ethtool_ops->set_fecparam(dev, &fec); 227 + if (ret < 0) 228 + goto out_ops; 229 + ethtool_notify(dev, ETHTOOL_MSG_FEC_NTF, NULL); 230 + 231 + out_ops: 232 + ethnl_ops_complete(dev); 233 + out_rtnl: 234 + rtnl_unlock(); 235 + out_dev: 236 + dev_put(dev); 237 + return ret; 238 + }
+19
net/ethtool/netlink.c
··· 244 244 [ETHTOOL_MSG_COALESCE_GET] = &ethnl_coalesce_request_ops, 245 245 [ETHTOOL_MSG_PAUSE_GET] = &ethnl_pause_request_ops, 246 246 [ETHTOOL_MSG_EEE_GET] = &ethnl_eee_request_ops, 247 + [ETHTOOL_MSG_FEC_GET] = &ethnl_fec_request_ops, 247 248 [ETHTOOL_MSG_TSINFO_GET] = &ethnl_tsinfo_request_ops, 248 249 }; 249 250 ··· 552 551 [ETHTOOL_MSG_COALESCE_NTF] = &ethnl_coalesce_request_ops, 553 552 [ETHTOOL_MSG_PAUSE_NTF] = &ethnl_pause_request_ops, 554 553 [ETHTOOL_MSG_EEE_NTF] = &ethnl_eee_request_ops, 554 + [ETHTOOL_MSG_FEC_NTF] = &ethnl_fec_request_ops, 555 555 }; 556 556 557 557 /* default notification handler */ ··· 645 643 [ETHTOOL_MSG_COALESCE_NTF] = ethnl_default_notify, 646 644 [ETHTOOL_MSG_PAUSE_NTF] = ethnl_default_notify, 647 645 [ETHTOOL_MSG_EEE_NTF] = ethnl_default_notify, 646 + [ETHTOOL_MSG_FEC_NTF] = ethnl_default_notify, 648 647 }; 649 648 650 649 void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) ··· 914 911 .dumpit = ethnl_tunnel_info_dumpit, 915 912 .policy = ethnl_tunnel_info_get_policy, 916 913 .maxattr = ARRAY_SIZE(ethnl_tunnel_info_get_policy) - 1, 914 + }, 915 + { 916 + .cmd = ETHTOOL_MSG_FEC_GET, 917 + .doit = ethnl_default_doit, 918 + .start = ethnl_default_start, 919 + .dumpit = ethnl_default_dumpit, 920 + .done = ethnl_default_done, 921 + .policy = ethnl_fec_get_policy, 922 + .maxattr = ARRAY_SIZE(ethnl_fec_get_policy) - 1, 923 + }, 924 + { 925 + .cmd = ETHTOOL_MSG_FEC_SET, 926 + .flags = GENL_UNS_ADMIN_PERM, 927 + .doit = ethnl_set_fec, 928 + .policy = ethnl_fec_set_policy, 929 + .maxattr = ARRAY_SIZE(ethnl_fec_set_policy) - 1, 917 930 }, 918 931 }; 919 932
+4
net/ethtool/netlink.h
··· 344 344 extern const struct ethnl_request_ops ethnl_pause_request_ops; 345 345 extern const struct ethnl_request_ops ethnl_eee_request_ops; 346 346 extern const struct ethnl_request_ops ethnl_tsinfo_request_ops; 347 + extern const struct ethnl_request_ops ethnl_fec_request_ops; 347 348 348 349 extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1]; 349 350 extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1]; ··· 376 375 extern const struct nla_policy ethnl_cable_test_act_policy[ETHTOOL_A_CABLE_TEST_HEADER + 1]; 377 376 extern const struct nla_policy ethnl_cable_test_tdr_act_policy[ETHTOOL_A_CABLE_TEST_TDR_CFG + 1]; 378 377 extern const struct nla_policy ethnl_tunnel_info_get_policy[ETHTOOL_A_TUNNEL_INFO_HEADER + 1]; 378 + extern const struct nla_policy ethnl_fec_get_policy[ETHTOOL_A_FEC_HEADER + 1]; 379 + extern const struct nla_policy ethnl_fec_set_policy[ETHTOOL_A_FEC_AUTO + 1]; 379 380 380 381 int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); 381 382 int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); ··· 395 392 int ethnl_tunnel_info_doit(struct sk_buff *skb, struct genl_info *info); 396 393 int ethnl_tunnel_info_start(struct netlink_callback *cb); 397 394 int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 395 + int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info); 398 396 399 397 #endif /* _NET_ETHTOOL_NETLINK_H */