Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ethtool/tsinfo.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
3
#include <linux/net_tstamp.h>
4
#include <linux/phy.h>
5
#include <linux/phy_link_topology.h>
6
#include <linux/ptp_clock_kernel.h>
7
#include <net/netdev_lock.h>
8
9
#include "netlink.h"
10
#include "common.h"
11
#include "bitset.h"
12
#include "ts.h"
13
14
struct tsinfo_req_info {
15
struct ethnl_req_info base;
16
struct hwtstamp_provider_desc hwprov_desc;
17
};
18
19
struct tsinfo_reply_data {
20
struct ethnl_reply_data base;
21
struct kernel_ethtool_ts_info ts_info;
22
struct ethtool_ts_stats stats;
23
};
24
25
#define TSINFO_REQINFO(__req_base) \
26
container_of(__req_base, struct tsinfo_req_info, base)
27
28
#define TSINFO_REPDATA(__reply_base) \
29
container_of(__reply_base, struct tsinfo_reply_data, base)
30
31
#define ETHTOOL_TS_STAT_CNT \
32
(__ETHTOOL_A_TS_STAT_CNT - (ETHTOOL_A_TS_STAT_UNSPEC + 1))
33
34
const struct nla_policy ethnl_tsinfo_get_policy[ETHTOOL_A_TSINFO_MAX + 1] = {
35
[ETHTOOL_A_TSINFO_HEADER] =
36
NLA_POLICY_NESTED(ethnl_header_policy_stats),
37
[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER] =
38
NLA_POLICY_NESTED(ethnl_ts_hwtst_prov_policy),
39
};
40
41
int ts_parse_hwtst_provider(const struct nlattr *nest,
42
struct hwtstamp_provider_desc *hwprov_desc,
43
struct netlink_ext_ack *extack,
44
bool *mod)
45
{
46
struct nlattr *tb[ARRAY_SIZE(ethnl_ts_hwtst_prov_policy)];
47
int ret;
48
49
ret = nla_parse_nested(tb,
50
ARRAY_SIZE(ethnl_ts_hwtst_prov_policy) - 1,
51
nest,
52
ethnl_ts_hwtst_prov_policy, extack);
53
if (ret < 0)
54
return ret;
55
56
if (NL_REQ_ATTR_CHECK(extack, nest, tb,
57
ETHTOOL_A_TS_HWTSTAMP_PROVIDER_INDEX) ||
58
NL_REQ_ATTR_CHECK(extack, nest, tb,
59
ETHTOOL_A_TS_HWTSTAMP_PROVIDER_QUALIFIER))
60
return -EINVAL;
61
62
ethnl_update_u32(&hwprov_desc->index,
63
tb[ETHTOOL_A_TS_HWTSTAMP_PROVIDER_INDEX],
64
mod);
65
ethnl_update_u32(&hwprov_desc->qualifier,
66
tb[ETHTOOL_A_TS_HWTSTAMP_PROVIDER_QUALIFIER],
67
mod);
68
69
return 0;
70
}
71
72
static int
73
tsinfo_parse_request(struct ethnl_req_info *req_base, struct nlattr **tb,
74
struct netlink_ext_ack *extack)
75
{
76
struct tsinfo_req_info *req = TSINFO_REQINFO(req_base);
77
bool mod = false;
78
79
req->hwprov_desc.index = -1;
80
81
if (!tb[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER])
82
return 0;
83
84
return ts_parse_hwtst_provider(tb[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER],
85
&req->hwprov_desc, extack, &mod);
86
}
87
88
static int tsinfo_prepare_data(const struct ethnl_req_info *req_base,
89
struct ethnl_reply_data *reply_base,
90
const struct genl_info *info)
91
{
92
struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
93
struct tsinfo_req_info *req = TSINFO_REQINFO(req_base);
94
struct net_device *dev = reply_base->dev;
95
int ret;
96
97
ret = ethnl_ops_begin(dev);
98
if (ret < 0)
99
return ret;
100
101
if (req->hwprov_desc.index != -1) {
102
ret = ethtool_get_ts_info_by_phc(dev, &data->ts_info,
103
&req->hwprov_desc);
104
ethnl_ops_complete(dev);
105
return ret;
106
}
107
108
if (req_base->flags & ETHTOOL_FLAG_STATS) {
109
ethtool_stats_init((u64 *)&data->stats,
110
sizeof(data->stats) / sizeof(u64));
111
if (dev->ethtool_ops->get_ts_stats)
112
dev->ethtool_ops->get_ts_stats(dev, &data->stats);
113
}
114
115
ret = __ethtool_get_ts_info(dev, &data->ts_info);
116
ethnl_ops_complete(dev);
117
118
return ret;
119
}
120
121
static int tsinfo_reply_size(const struct ethnl_req_info *req_base,
122
const struct ethnl_reply_data *reply_base)
123
{
124
const struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
125
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
126
const struct kernel_ethtool_ts_info *ts_info = &data->ts_info;
127
int len = 0;
128
int ret;
129
130
BUILD_BUG_ON(__SOF_TIMESTAMPING_CNT > 32);
131
BUILD_BUG_ON(__HWTSTAMP_TX_CNT > 32);
132
BUILD_BUG_ON(__HWTSTAMP_FILTER_CNT > 32);
133
134
if (ts_info->so_timestamping) {
135
ret = ethnl_bitset32_size(&ts_info->so_timestamping, NULL,
136
__SOF_TIMESTAMPING_CNT,
137
sof_timestamping_names, compact);
138
if (ret < 0)
139
return ret;
140
len += ret; /* _TSINFO_TIMESTAMPING */
141
}
142
if (ts_info->tx_types) {
143
ret = ethnl_bitset32_size(&ts_info->tx_types, NULL,
144
__HWTSTAMP_TX_CNT,
145
ts_tx_type_names, compact);
146
if (ret < 0)
147
return ret;
148
len += ret; /* _TSINFO_TX_TYPES */
149
}
150
if (ts_info->rx_filters) {
151
ret = ethnl_bitset32_size(&ts_info->rx_filters, NULL,
152
__HWTSTAMP_FILTER_CNT,
153
ts_rx_filter_names, compact);
154
if (ret < 0)
155
return ret;
156
len += ret; /* _TSINFO_RX_FILTERS */
157
}
158
if (ts_info->phc_index >= 0) {
159
len += nla_total_size(sizeof(u32)); /* _TSINFO_PHC_INDEX */
160
/* _TSINFO_HWTSTAMP_PROVIDER */
161
len += nla_total_size(0) + 2 * nla_total_size(sizeof(u32));
162
}
163
if (ts_info->phc_source) {
164
len += nla_total_size(sizeof(u32)); /* _TSINFO_HWTSTAMP_SOURCE */
165
if (ts_info->phc_phyindex)
166
/* _TSINFO_HWTSTAMP_PHYINDEX */
167
len += nla_total_size(sizeof(u32));
168
}
169
if (req_base->flags & ETHTOOL_FLAG_STATS)
170
len += nla_total_size(0) + /* _TSINFO_STATS */
171
nla_total_size_64bit(sizeof(u64)) * ETHTOOL_TS_STAT_CNT;
172
173
return len;
174
}
175
176
static int tsinfo_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)
177
{
178
if (val == ETHTOOL_STAT_NOT_SET)
179
return 0;
180
if (nla_put_uint(skb, attrtype, val))
181
return -EMSGSIZE;
182
return 0;
183
}
184
185
static int tsinfo_put_stats(struct sk_buff *skb,
186
const struct ethtool_ts_stats *stats)
187
{
188
struct nlattr *nest;
189
190
nest = nla_nest_start(skb, ETHTOOL_A_TSINFO_STATS);
191
if (!nest)
192
return -EMSGSIZE;
193
194
if (tsinfo_put_stat(skb, stats->tx_stats.pkts,
195
ETHTOOL_A_TS_STAT_TX_PKTS) ||
196
tsinfo_put_stat(skb, stats->tx_stats.onestep_pkts_unconfirmed,
197
ETHTOOL_A_TS_STAT_TX_ONESTEP_PKTS_UNCONFIRMED) ||
198
tsinfo_put_stat(skb, stats->tx_stats.lost,
199
ETHTOOL_A_TS_STAT_TX_LOST) ||
200
tsinfo_put_stat(skb, stats->tx_stats.err,
201
ETHTOOL_A_TS_STAT_TX_ERR))
202
goto err_cancel;
203
204
nla_nest_end(skb, nest);
205
return 0;
206
207
err_cancel:
208
nla_nest_cancel(skb, nest);
209
return -EMSGSIZE;
210
}
211
212
static int tsinfo_fill_reply(struct sk_buff *skb,
213
const struct ethnl_req_info *req_base,
214
const struct ethnl_reply_data *reply_base)
215
{
216
const struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
217
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
218
const struct kernel_ethtool_ts_info *ts_info = &data->ts_info;
219
int ret;
220
221
if (ts_info->so_timestamping) {
222
ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_TIMESTAMPING,
223
&ts_info->so_timestamping, NULL,
224
__SOF_TIMESTAMPING_CNT,
225
sof_timestamping_names, compact);
226
if (ret < 0)
227
return ret;
228
}
229
if (ts_info->tx_types) {
230
ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_TX_TYPES,
231
&ts_info->tx_types, NULL,
232
__HWTSTAMP_TX_CNT,
233
ts_tx_type_names, compact);
234
if (ret < 0)
235
return ret;
236
}
237
if (ts_info->rx_filters) {
238
ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_RX_FILTERS,
239
&ts_info->rx_filters, NULL,
240
__HWTSTAMP_FILTER_CNT,
241
ts_rx_filter_names, compact);
242
if (ret < 0)
243
return ret;
244
}
245
if (ts_info->phc_index >= 0) {
246
struct nlattr *nest;
247
248
ret = nla_put_u32(skb, ETHTOOL_A_TSINFO_PHC_INDEX,
249
ts_info->phc_index);
250
if (ret)
251
return -EMSGSIZE;
252
253
nest = nla_nest_start(skb, ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER);
254
if (!nest)
255
return -EMSGSIZE;
256
257
if (nla_put_u32(skb, ETHTOOL_A_TS_HWTSTAMP_PROVIDER_INDEX,
258
ts_info->phc_index) ||
259
nla_put_u32(skb,
260
ETHTOOL_A_TS_HWTSTAMP_PROVIDER_QUALIFIER,
261
ts_info->phc_qualifier)) {
262
nla_nest_cancel(skb, nest);
263
return -EMSGSIZE;
264
}
265
266
nla_nest_end(skb, nest);
267
}
268
if (ts_info->phc_source) {
269
if (nla_put_u32(skb, ETHTOOL_A_TSINFO_HWTSTAMP_SOURCE,
270
ts_info->phc_source))
271
return -EMSGSIZE;
272
273
if (ts_info->phc_phyindex &&
274
nla_put_u32(skb, ETHTOOL_A_TSINFO_HWTSTAMP_PHYINDEX,
275
ts_info->phc_phyindex))
276
return -EMSGSIZE;
277
}
278
if (req_base->flags & ETHTOOL_FLAG_STATS &&
279
tsinfo_put_stats(skb, &data->stats))
280
return -EMSGSIZE;
281
282
return 0;
283
}
284
285
struct ethnl_tsinfo_dump_ctx {
286
struct tsinfo_req_info *req_info;
287
struct tsinfo_reply_data *reply_data;
288
unsigned long pos_ifindex;
289
bool netdev_dump_done;
290
unsigned long pos_phyindex;
291
enum hwtstamp_provider_qualifier pos_phcqualifier;
292
};
293
294
static void *ethnl_tsinfo_prepare_dump(struct sk_buff *skb,
295
struct net_device *dev,
296
struct tsinfo_reply_data *reply_data,
297
struct netlink_callback *cb)
298
{
299
struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx;
300
void *ehdr = NULL;
301
302
ehdr = ethnl_dump_put(skb, cb,
303
ETHTOOL_MSG_TSINFO_GET_REPLY);
304
if (!ehdr)
305
return ERR_PTR(-EMSGSIZE);
306
307
reply_data = ctx->reply_data;
308
memset(reply_data, 0, sizeof(*reply_data));
309
reply_data->base.dev = dev;
310
reply_data->ts_info.cmd = ETHTOOL_GET_TS_INFO;
311
reply_data->ts_info.phc_index = -1;
312
313
return ehdr;
314
}
315
316
static int ethnl_tsinfo_end_dump(struct sk_buff *skb,
317
struct net_device *dev,
318
struct tsinfo_req_info *req_info,
319
struct tsinfo_reply_data *reply_data,
320
void *ehdr)
321
{
322
int ret;
323
324
reply_data->ts_info.so_timestamping |= SOF_TIMESTAMPING_RX_SOFTWARE |
325
SOF_TIMESTAMPING_SOFTWARE;
326
327
ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_TSINFO_HEADER);
328
if (ret < 0)
329
return ret;
330
331
ret = tsinfo_fill_reply(skb, &req_info->base, &reply_data->base);
332
if (ret < 0)
333
return ret;
334
335
reply_data->base.dev = NULL;
336
genlmsg_end(skb, ehdr);
337
338
return ret;
339
}
340
341
static int ethnl_tsinfo_dump_one_phydev(struct sk_buff *skb,
342
struct net_device *dev,
343
struct phy_device *phydev,
344
struct netlink_callback *cb)
345
{
346
struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx;
347
struct tsinfo_reply_data *reply_data;
348
struct tsinfo_req_info *req_info;
349
void *ehdr = NULL;
350
int ret = 0;
351
352
if (!phy_has_tsinfo(phydev))
353
return -EOPNOTSUPP;
354
355
reply_data = ctx->reply_data;
356
req_info = ctx->req_info;
357
ehdr = ethnl_tsinfo_prepare_dump(skb, dev, reply_data, cb);
358
if (IS_ERR(ehdr))
359
return PTR_ERR(ehdr);
360
361
ret = phy_ts_info(phydev, &reply_data->ts_info);
362
if (ret < 0)
363
goto err;
364
365
if (reply_data->ts_info.phc_index >= 0) {
366
reply_data->ts_info.phc_source = HWTSTAMP_SOURCE_PHYLIB;
367
reply_data->ts_info.phc_phyindex = phydev->phyindex;
368
}
369
370
ret = ethnl_tsinfo_end_dump(skb, dev, req_info, reply_data, ehdr);
371
if (ret < 0)
372
goto err;
373
374
return ret;
375
err:
376
genlmsg_cancel(skb, ehdr);
377
return ret;
378
}
379
380
static int ethnl_tsinfo_dump_one_netdev(struct sk_buff *skb,
381
struct net_device *dev,
382
struct netlink_callback *cb)
383
{
384
struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx;
385
const struct ethtool_ops *ops = dev->ethtool_ops;
386
struct tsinfo_reply_data *reply_data;
387
struct tsinfo_req_info *req_info;
388
void *ehdr = NULL;
389
int ret = 0;
390
391
if (!ops->get_ts_info)
392
return -EOPNOTSUPP;
393
394
reply_data = ctx->reply_data;
395
req_info = ctx->req_info;
396
for (; ctx->pos_phcqualifier < HWTSTAMP_PROVIDER_QUALIFIER_CNT;
397
ctx->pos_phcqualifier++) {
398
if (!net_support_hwtstamp_qualifier(dev,
399
ctx->pos_phcqualifier))
400
continue;
401
402
ehdr = ethnl_tsinfo_prepare_dump(skb, dev, reply_data, cb);
403
if (IS_ERR(ehdr)) {
404
ret = PTR_ERR(ehdr);
405
goto err;
406
}
407
408
reply_data->ts_info.phc_qualifier = ctx->pos_phcqualifier;
409
ret = ops->get_ts_info(dev, &reply_data->ts_info);
410
if (ret < 0)
411
goto err;
412
413
if (reply_data->ts_info.phc_index >= 0)
414
reply_data->ts_info.phc_source = HWTSTAMP_SOURCE_NETDEV;
415
ret = ethnl_tsinfo_end_dump(skb, dev, req_info, reply_data,
416
ehdr);
417
if (ret < 0)
418
goto err;
419
}
420
421
return ret;
422
423
err:
424
genlmsg_cancel(skb, ehdr);
425
return ret;
426
}
427
428
static int ethnl_tsinfo_dump_one_net_topo(struct sk_buff *skb,
429
struct net_device *dev,
430
struct netlink_callback *cb)
431
{
432
struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx;
433
struct phy_device_node *pdn;
434
int ret = 0;
435
436
if (!ctx->netdev_dump_done) {
437
ret = ethnl_tsinfo_dump_one_netdev(skb, dev, cb);
438
if (ret < 0 && ret != -EOPNOTSUPP)
439
return ret;
440
ctx->netdev_dump_done = true;
441
}
442
443
if (!dev->link_topo) {
444
if (phy_has_tsinfo(dev->phydev)) {
445
ret = ethnl_tsinfo_dump_one_phydev(skb, dev,
446
dev->phydev, cb);
447
if (ret < 0 && ret != -EOPNOTSUPP)
448
return ret;
449
}
450
451
return 0;
452
}
453
454
xa_for_each_start(&dev->link_topo->phys, ctx->pos_phyindex, pdn,
455
ctx->pos_phyindex) {
456
if (phy_has_tsinfo(pdn->phy)) {
457
ret = ethnl_tsinfo_dump_one_phydev(skb, dev,
458
pdn->phy, cb);
459
if (ret < 0 && ret != -EOPNOTSUPP)
460
return ret;
461
}
462
}
463
464
return ret;
465
}
466
467
int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
468
{
469
struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx;
470
struct net *net = sock_net(skb->sk);
471
struct net_device *dev;
472
int ret = 0;
473
474
rtnl_lock();
475
if (ctx->req_info->base.dev) {
476
dev = ctx->req_info->base.dev;
477
netdev_lock_ops(dev);
478
ret = ethnl_tsinfo_dump_one_net_topo(skb, dev, cb);
479
netdev_unlock_ops(dev);
480
} else {
481
for_each_netdev_dump(net, dev, ctx->pos_ifindex) {
482
netdev_lock_ops(dev);
483
ret = ethnl_tsinfo_dump_one_net_topo(skb, dev, cb);
484
netdev_unlock_ops(dev);
485
if (ret < 0 && ret != -EOPNOTSUPP)
486
break;
487
ctx->pos_phyindex = 0;
488
ctx->netdev_dump_done = false;
489
ctx->pos_phcqualifier = HWTSTAMP_PROVIDER_QUALIFIER_PRECISE;
490
}
491
}
492
rtnl_unlock();
493
494
return ret;
495
}
496
497
int ethnl_tsinfo_start(struct netlink_callback *cb)
498
{
499
const struct genl_dumpit_info *info = genl_dumpit_info(cb);
500
struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx;
501
struct nlattr **tb = info->info.attrs;
502
struct tsinfo_reply_data *reply_data;
503
struct tsinfo_req_info *req_info;
504
int ret;
505
506
BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
507
508
req_info = kzalloc(sizeof(*req_info), GFP_KERNEL);
509
if (!req_info)
510
return -ENOMEM;
511
reply_data = kzalloc(sizeof(*reply_data), GFP_KERNEL);
512
if (!reply_data) {
513
ret = -ENOMEM;
514
goto free_req_info;
515
}
516
517
ret = ethnl_parse_header_dev_get(&req_info->base,
518
tb[ETHTOOL_A_TSINFO_HEADER],
519
sock_net(cb->skb->sk), cb->extack,
520
false);
521
if (ret < 0)
522
goto free_reply_data;
523
524
ctx->req_info = req_info;
525
ctx->reply_data = reply_data;
526
ctx->pos_ifindex = 0;
527
ctx->pos_phyindex = 0;
528
ctx->netdev_dump_done = false;
529
ctx->pos_phcqualifier = HWTSTAMP_PROVIDER_QUALIFIER_PRECISE;
530
531
return 0;
532
533
free_reply_data:
534
kfree(reply_data);
535
free_req_info:
536
kfree(req_info);
537
538
return ret;
539
}
540
541
int ethnl_tsinfo_done(struct netlink_callback *cb)
542
{
543
struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx;
544
struct tsinfo_req_info *req_info = ctx->req_info;
545
546
ethnl_parse_header_dev_put(&req_info->base);
547
kfree(ctx->reply_data);
548
kfree(ctx->req_info);
549
550
return 0;
551
}
552
553
const struct ethnl_request_ops ethnl_tsinfo_request_ops = {
554
.request_cmd = ETHTOOL_MSG_TSINFO_GET,
555
.reply_cmd = ETHTOOL_MSG_TSINFO_GET_REPLY,
556
.hdr_attr = ETHTOOL_A_TSINFO_HEADER,
557
.req_info_size = sizeof(struct tsinfo_req_info),
558
.reply_data_size = sizeof(struct tsinfo_reply_data),
559
560
.parse_request = tsinfo_parse_request,
561
.prepare_data = tsinfo_prepare_data,
562
.reply_size = tsinfo_reply_size,
563
.fill_reply = tsinfo_fill_reply,
564
};
565
566