Skip to content
/ linux Public

Commit 1e5d1f6

Browse files
kuba-moodavem330
authored andcommitted
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>
1 parent 2811005 commit 1e5d1f6

File tree

6 files changed

+339
-3
lines changed

6 files changed

+339
-3
lines changed

Documentation/networking/ethtool-netlink.rst

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ Userspace to kernel:
208208
``ETHTOOL_MSG_CABLE_TEST_ACT`` action start cable test
209209
``ETHTOOL_MSG_CABLE_TEST_TDR_ACT`` action start raw TDR cable test
210210
``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
211213
===================================== ================================
212214

213215
Kernel to userspace:
@@ -242,6 +244,8 @@ Kernel to userspace:
242244
``ETHTOOL_MSG_CABLE_TEST_NTF`` Cable test results
243245
``ETHTOOL_MSG_CABLE_TEST_TDR_NTF`` Cable test TDR results
244246
``ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY`` tunnel offload info
247+
``ETHTOOL_MSG_FEC_GET_REPLY`` FEC settings
248+
``ETHTOOL_MSG_FEC_NTF`` FEC settings
245249
===================================== =================================
246250

247251
``GET`` requests are sent by userspace applications to retrieve device
@@ -1280,6 +1284,60 @@ Kernel response contents:
12801284
For UDP tunnel table empty ``ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES`` indicates that
12811285
the table contains static entries, hard-coded by the NIC.
12821286

1287+
FEC_GET
1288+
=======
1289+
1290+
Gets FEC configuration and state like ``ETHTOOL_GFECPARAM`` ioctl request.
1291+
1292+
Request contents:
1293+
1294+
===================================== ====== ==========================
1295+
``ETHTOOL_A_FEC_HEADER`` nested request header
1296+
===================================== ====== ==========================
1297+
1298+
Kernel response contents:
1299+
1300+
===================================== ====== ==========================
1301+
``ETHTOOL_A_FEC_HEADER`` nested request header
1302+
``ETHTOOL_A_FEC_MODES`` bitset configured modes
1303+
``ETHTOOL_A_FEC_AUTO`` bool FEC mode auto selection
1304+
``ETHTOOL_A_FEC_ACTIVE`` u32 index of active FEC mode
1305+
===================================== ====== ==========================
1306+
1307+
``ETHTOOL_A_FEC_ACTIVE`` is the bit index of the FEC link mode currently
1308+
active on the interface. This attribute may not be present if device does
1309+
not support FEC.
1310+
1311+
``ETHTOOL_A_FEC_MODES`` and ``ETHTOOL_A_FEC_AUTO`` are only meaningful when
1312+
autonegotiation is disabled. If ``ETHTOOL_A_FEC_AUTO`` is non-zero driver will
1313+
select the FEC mode automatically based on the parameters of the SFP module.
1314+
This is equivalent to the ``ETHTOOL_FEC_AUTO`` bit of the ioctl interface.
1315+
``ETHTOOL_A_FEC_MODES`` carry the current FEC configuration using link mode
1316+
bits (rather than old ``ETHTOOL_FEC_*`` bits).
1317+
1318+
FEC_SET
1319+
=======
1320+
1321+
Sets FEC parameters like ``ETHTOOL_SFECPARAM`` ioctl request.
1322+
1323+
Request contents:
1324+
1325+
===================================== ====== ==========================
1326+
``ETHTOOL_A_FEC_HEADER`` nested request header
1327+
``ETHTOOL_A_FEC_MODES`` bitset configured modes
1328+
``ETHTOOL_A_FEC_AUTO`` bool FEC mode auto selection
1329+
===================================== ====== ==========================
1330+
1331+
``FEC_SET`` is only meaningful when autonegotiation is disabled. Otherwise
1332+
FEC mode is selected as part of autonegotiation.
1333+
1334+
``ETHTOOL_A_FEC_MODES`` selects which FEC mode should be used. It's recommended
1335+
to set only one bit, if multiple bits are set driver may choose between them
1336+
in an implementation specific way.
1337+
1338+
``ETHTOOL_A_FEC_AUTO`` requests the driver to choose FEC mode based on SFP
1339+
module parameters. This does not mean autonegotiation.
1340+
12831341
Request translation
12841342
===================
12851343

@@ -1373,8 +1431,8 @@ are netlink only.
13731431
``ETHTOOL_MSG_LINKMODES_SET``
13741432
``ETHTOOL_PHY_GTUNABLE`` n/a
13751433
``ETHTOOL_PHY_STUNABLE`` n/a
1376-
``ETHTOOL_GFECPARAM`` n/a
1377-
``ETHTOOL_SFECPARAM`` n/a
1434+
``ETHTOOL_GFECPARAM`` ``ETHTOOL_MSG_FEC_GET``
1435+
``ETHTOOL_SFECPARAM`` ``ETHTOOL_MSG_FEC_SET``
13781436
n/a ''ETHTOOL_MSG_CABLE_TEST_ACT''
13791437
n/a ''ETHTOOL_MSG_CABLE_TEST_TDR_ACT''
13801438
n/a ``ETHTOOL_MSG_TUNNEL_INFO_GET``

include/uapi/linux/ethtool_netlink.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ enum {
4242
ETHTOOL_MSG_CABLE_TEST_ACT,
4343
ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
4444
ETHTOOL_MSG_TUNNEL_INFO_GET,
45+
ETHTOOL_MSG_FEC_GET,
46+
ETHTOOL_MSG_FEC_SET,
4547

4648
/* add new constants above here */
4749
__ETHTOOL_MSG_USER_CNT,
@@ -80,6 +82,8 @@ enum {
8082
ETHTOOL_MSG_CABLE_TEST_NTF,
8183
ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
8284
ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY,
85+
ETHTOOL_MSG_FEC_GET_REPLY,
86+
ETHTOOL_MSG_FEC_NTF,
8387

8488
/* add new constants above here */
8589
__ETHTOOL_MSG_KERNEL_CNT,
@@ -629,6 +633,19 @@ enum {
629633
ETHTOOL_A_TUNNEL_INFO_MAX = (__ETHTOOL_A_TUNNEL_INFO_CNT - 1)
630634
};
631635

636+
/* FEC */
637+
638+
enum {
639+
ETHTOOL_A_FEC_UNSPEC,
640+
ETHTOOL_A_FEC_HEADER, /* nest - _A_HEADER_* */
641+
ETHTOOL_A_FEC_MODES, /* bitset */
642+
ETHTOOL_A_FEC_AUTO, /* u8 */
643+
ETHTOOL_A_FEC_ACTIVE, /* u32 */
644+
645+
__ETHTOOL_A_FEC_CNT,
646+
ETHTOOL_A_FEC_MAX = (__ETHTOOL_A_FEC_CNT - 1)
647+
};
648+
632649
/* generic netlink info */
633650
#define ETHTOOL_GENL_NAME "ethtool"
634651
#define ETHTOOL_GENL_VERSION 1

net/ethtool/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o
77
ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \
88
linkstate.o debug.o wol.o features.o privflags.o rings.o \
99
channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
10-
tunnels.o
10+
tunnels.o fec.o

net/ethtool/fec.c

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
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+
}

0 commit comments

Comments
 (0)