Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ethtool/fec.c
29269 views
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
struct fec_stat_grp {
17
u64 stats[1 + ETHTOOL_MAX_LANES];
18
u8 cnt;
19
} corr, uncorr, corr_bits;
20
struct ethtool_fec_hist fec_stat_hist;
21
};
22
23
#define FEC_REPDATA(__reply_base) \
24
container_of(__reply_base, struct fec_reply_data, base)
25
26
#define ETHTOOL_FEC_MASK ((ETHTOOL_FEC_LLRS << 1) - 1)
27
28
const struct nla_policy ethnl_fec_get_policy[ETHTOOL_A_FEC_HEADER + 1] = {
29
[ETHTOOL_A_FEC_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_stats),
30
};
31
32
static void
33
ethtool_fec_to_link_modes(u32 fec, unsigned long *link_modes, u8 *fec_auto)
34
{
35
if (fec_auto)
36
*fec_auto = !!(fec & ETHTOOL_FEC_AUTO);
37
38
if (fec & ETHTOOL_FEC_OFF)
39
__set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, link_modes);
40
if (fec & ETHTOOL_FEC_RS)
41
__set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, link_modes);
42
if (fec & ETHTOOL_FEC_BASER)
43
__set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, link_modes);
44
if (fec & ETHTOOL_FEC_LLRS)
45
__set_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, link_modes);
46
}
47
48
static int
49
ethtool_link_modes_to_fecparam(struct ethtool_fecparam *fec,
50
unsigned long *link_modes, u8 fec_auto)
51
{
52
memset(fec, 0, sizeof(*fec));
53
54
if (fec_auto)
55
fec->fec |= ETHTOOL_FEC_AUTO;
56
57
if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, link_modes))
58
fec->fec |= ETHTOOL_FEC_OFF;
59
if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, link_modes))
60
fec->fec |= ETHTOOL_FEC_RS;
61
if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, link_modes))
62
fec->fec |= ETHTOOL_FEC_BASER;
63
if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, link_modes))
64
fec->fec |= ETHTOOL_FEC_LLRS;
65
66
if (!bitmap_empty(link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS))
67
return -EINVAL;
68
69
return 0;
70
}
71
72
static void
73
fec_stats_recalc(struct fec_stat_grp *grp, struct ethtool_fec_stat *stats)
74
{
75
int i;
76
77
if (stats->lanes[0] == ETHTOOL_STAT_NOT_SET) {
78
grp->stats[0] = stats->total;
79
grp->cnt = stats->total != ETHTOOL_STAT_NOT_SET;
80
return;
81
}
82
83
grp->cnt = 1;
84
grp->stats[0] = 0;
85
for (i = 0; i < ETHTOOL_MAX_LANES; i++) {
86
if (stats->lanes[i] == ETHTOOL_STAT_NOT_SET)
87
break;
88
89
grp->stats[0] += stats->lanes[i];
90
grp->stats[grp->cnt++] = stats->lanes[i];
91
}
92
}
93
94
static int fec_prepare_data(const struct ethnl_req_info *req_base,
95
struct ethnl_reply_data *reply_base,
96
const struct genl_info *info)
97
{
98
__ETHTOOL_DECLARE_LINK_MODE_MASK(active_fec_modes) = {};
99
struct fec_reply_data *data = FEC_REPDATA(reply_base);
100
struct net_device *dev = reply_base->dev;
101
struct ethtool_fecparam fec = {};
102
int ret;
103
104
if (!dev->ethtool_ops->get_fecparam)
105
return -EOPNOTSUPP;
106
ret = ethnl_ops_begin(dev);
107
if (ret < 0)
108
return ret;
109
ret = dev->ethtool_ops->get_fecparam(dev, &fec);
110
if (ret)
111
goto out_complete;
112
if (req_base->flags & ETHTOOL_FLAG_STATS &&
113
dev->ethtool_ops->get_fec_stats) {
114
struct ethtool_fec_stats stats;
115
116
ethtool_stats_init((u64 *)&stats, sizeof(stats) / 8);
117
ethtool_stats_init((u64 *)data->fec_stat_hist.values,
118
sizeof(data->fec_stat_hist.values) / 8);
119
dev->ethtool_ops->get_fec_stats(dev, &stats,
120
&data->fec_stat_hist);
121
122
fec_stats_recalc(&data->corr, &stats.corrected_blocks);
123
fec_stats_recalc(&data->uncorr, &stats.uncorrectable_blocks);
124
fec_stats_recalc(&data->corr_bits, &stats.corrected_bits);
125
}
126
127
WARN_ON_ONCE(fec.reserved);
128
129
ethtool_fec_to_link_modes(fec.fec, data->fec_link_modes,
130
&data->fec_auto);
131
132
ethtool_fec_to_link_modes(fec.active_fec, active_fec_modes, NULL);
133
data->active_fec = find_first_bit(active_fec_modes,
134
__ETHTOOL_LINK_MODE_MASK_NBITS);
135
/* Don't report attr if no FEC mode set. Note that
136
* ethtool_fecparam_to_link_modes() ignores NONE and AUTO.
137
*/
138
if (data->active_fec == __ETHTOOL_LINK_MODE_MASK_NBITS)
139
data->active_fec = 0;
140
141
out_complete:
142
ethnl_ops_complete(dev);
143
return ret;
144
}
145
146
static int fec_reply_size(const struct ethnl_req_info *req_base,
147
const struct ethnl_reply_data *reply_base)
148
{
149
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
150
const struct fec_reply_data *data = FEC_REPDATA(reply_base);
151
int len = 0;
152
int ret;
153
154
ret = ethnl_bitset_size(data->fec_link_modes, NULL,
155
__ETHTOOL_LINK_MODE_MASK_NBITS,
156
link_mode_names, compact);
157
if (ret < 0)
158
return ret;
159
len += ret;
160
161
len += nla_total_size(sizeof(u8)) + /* _FEC_AUTO */
162
nla_total_size(sizeof(u32)); /* _FEC_ACTIVE */
163
164
if (req_base->flags & ETHTOOL_FLAG_STATS) {
165
len += 3 * nla_total_size_64bit(sizeof(u64) *
166
(1 + ETHTOOL_MAX_LANES));
167
/* add FEC bins information */
168
len += (nla_total_size(0) + /* _A_FEC_HIST */
169
nla_total_size(4) + /* _A_FEC_HIST_BIN_LOW */
170
nla_total_size(4) + /* _A_FEC_HIST_BIN_HI */
171
/* _A_FEC_HIST_BIN_VAL + per-lane values */
172
nla_total_size_64bit(sizeof(u64)) +
173
nla_total_size_64bit(sizeof(u64) * ETHTOOL_MAX_LANES)) *
174
ETHTOOL_FEC_HIST_MAX;
175
}
176
177
return len;
178
}
179
180
static int fec_put_hist(struct sk_buff *skb,
181
const struct ethtool_fec_hist *hist)
182
{
183
const struct ethtool_fec_hist_range *ranges = hist->ranges;
184
const struct ethtool_fec_hist_value *values = hist->values;
185
struct nlattr *nest;
186
int i, j;
187
u64 sum;
188
189
if (!ranges)
190
return 0;
191
192
for (i = 0; i < ETHTOOL_FEC_HIST_MAX; i++) {
193
if (i && !ranges[i].low && !ranges[i].high)
194
break;
195
196
if (WARN_ON_ONCE(values[i].sum == ETHTOOL_STAT_NOT_SET &&
197
values[i].per_lane[0] == ETHTOOL_STAT_NOT_SET))
198
break;
199
200
nest = nla_nest_start(skb, ETHTOOL_A_FEC_STAT_HIST);
201
if (!nest)
202
return -EMSGSIZE;
203
204
if (nla_put_u32(skb, ETHTOOL_A_FEC_HIST_BIN_LOW,
205
ranges[i].low) ||
206
nla_put_u32(skb, ETHTOOL_A_FEC_HIST_BIN_HIGH,
207
ranges[i].high))
208
goto err_cancel_hist;
209
sum = 0;
210
for (j = 0; j < ETHTOOL_MAX_LANES; j++) {
211
if (values[i].per_lane[j] == ETHTOOL_STAT_NOT_SET)
212
break;
213
sum += values[i].per_lane[j];
214
}
215
if (nla_put_uint(skb, ETHTOOL_A_FEC_HIST_BIN_VAL,
216
values[i].sum == ETHTOOL_STAT_NOT_SET ?
217
sum : values[i].sum))
218
goto err_cancel_hist;
219
if (j && nla_put_64bit(skb, ETHTOOL_A_FEC_HIST_BIN_VAL_PER_LANE,
220
sizeof(u64) * j,
221
values[i].per_lane,
222
ETHTOOL_A_FEC_HIST_PAD))
223
goto err_cancel_hist;
224
225
nla_nest_end(skb, nest);
226
}
227
228
return 0;
229
230
err_cancel_hist:
231
nla_nest_cancel(skb, nest);
232
return -EMSGSIZE;
233
}
234
235
static int fec_put_stats(struct sk_buff *skb, const struct fec_reply_data *data)
236
{
237
struct nlattr *nest;
238
239
nest = nla_nest_start(skb, ETHTOOL_A_FEC_STATS);
240
if (!nest)
241
return -EMSGSIZE;
242
243
if (nla_put_64bit(skb, ETHTOOL_A_FEC_STAT_CORRECTED,
244
sizeof(u64) * data->corr.cnt,
245
data->corr.stats, ETHTOOL_A_FEC_STAT_PAD) ||
246
nla_put_64bit(skb, ETHTOOL_A_FEC_STAT_UNCORR,
247
sizeof(u64) * data->uncorr.cnt,
248
data->uncorr.stats, ETHTOOL_A_FEC_STAT_PAD) ||
249
nla_put_64bit(skb, ETHTOOL_A_FEC_STAT_CORR_BITS,
250
sizeof(u64) * data->corr_bits.cnt,
251
data->corr_bits.stats, ETHTOOL_A_FEC_STAT_PAD))
252
goto err_cancel;
253
254
if (fec_put_hist(skb, &data->fec_stat_hist))
255
goto err_cancel;
256
257
nla_nest_end(skb, nest);
258
return 0;
259
260
err_cancel:
261
nla_nest_cancel(skb, nest);
262
return -EMSGSIZE;
263
}
264
265
static int fec_fill_reply(struct sk_buff *skb,
266
const struct ethnl_req_info *req_base,
267
const struct ethnl_reply_data *reply_base)
268
{
269
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
270
const struct fec_reply_data *data = FEC_REPDATA(reply_base);
271
int ret;
272
273
ret = ethnl_put_bitset(skb, ETHTOOL_A_FEC_MODES,
274
data->fec_link_modes, NULL,
275
__ETHTOOL_LINK_MODE_MASK_NBITS,
276
link_mode_names, compact);
277
if (ret < 0)
278
return ret;
279
280
if (nla_put_u8(skb, ETHTOOL_A_FEC_AUTO, data->fec_auto) ||
281
(data->active_fec &&
282
nla_put_u32(skb, ETHTOOL_A_FEC_ACTIVE, data->active_fec)))
283
return -EMSGSIZE;
284
285
if (req_base->flags & ETHTOOL_FLAG_STATS && fec_put_stats(skb, data))
286
return -EMSGSIZE;
287
288
return 0;
289
}
290
291
/* FEC_SET */
292
293
const struct nla_policy ethnl_fec_set_policy[ETHTOOL_A_FEC_AUTO + 1] = {
294
[ETHTOOL_A_FEC_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
295
[ETHTOOL_A_FEC_MODES] = { .type = NLA_NESTED },
296
[ETHTOOL_A_FEC_AUTO] = NLA_POLICY_MAX(NLA_U8, 1),
297
};
298
299
static int
300
ethnl_set_fec_validate(struct ethnl_req_info *req_info, struct genl_info *info)
301
{
302
const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
303
304
return ops->get_fecparam && ops->set_fecparam ? 1 : -EOPNOTSUPP;
305
}
306
307
static int
308
ethnl_set_fec(struct ethnl_req_info *req_info, struct genl_info *info)
309
{
310
__ETHTOOL_DECLARE_LINK_MODE_MASK(fec_link_modes) = {};
311
struct net_device *dev = req_info->dev;
312
struct nlattr **tb = info->attrs;
313
struct ethtool_fecparam fec = {};
314
bool mod = false;
315
u8 fec_auto;
316
int ret;
317
318
ret = dev->ethtool_ops->get_fecparam(dev, &fec);
319
if (ret < 0)
320
return ret;
321
322
ethtool_fec_to_link_modes(fec.fec, fec_link_modes, &fec_auto);
323
324
ret = ethnl_update_bitset(fec_link_modes,
325
__ETHTOOL_LINK_MODE_MASK_NBITS,
326
tb[ETHTOOL_A_FEC_MODES],
327
link_mode_names, info->extack, &mod);
328
if (ret < 0)
329
return ret;
330
ethnl_update_u8(&fec_auto, tb[ETHTOOL_A_FEC_AUTO], &mod);
331
if (!mod)
332
return 0;
333
334
ret = ethtool_link_modes_to_fecparam(&fec, fec_link_modes, fec_auto);
335
if (ret) {
336
NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_FEC_MODES],
337
"invalid FEC modes requested");
338
return ret;
339
}
340
if (!fec.fec) {
341
NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_FEC_MODES],
342
"no FEC modes set");
343
return -EINVAL;
344
}
345
346
ret = dev->ethtool_ops->set_fecparam(dev, &fec);
347
return ret < 0 ? ret : 1;
348
}
349
350
const struct ethnl_request_ops ethnl_fec_request_ops = {
351
.request_cmd = ETHTOOL_MSG_FEC_GET,
352
.reply_cmd = ETHTOOL_MSG_FEC_GET_REPLY,
353
.hdr_attr = ETHTOOL_A_FEC_HEADER,
354
.req_info_size = sizeof(struct fec_req_info),
355
.reply_data_size = sizeof(struct fec_reply_data),
356
357
.prepare_data = fec_prepare_data,
358
.reply_size = fec_reply_size,
359
.fill_reply = fec_fill_reply,
360
361
.set_validate = ethnl_set_fec_validate,
362
.set = ethnl_set_fec,
363
.set_ntf_cmd = ETHTOOL_MSG_FEC_NTF,
364
};
365
366