tsinfo.c 14 KB

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