tsinfo.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <linux/net_tstamp.h>
  3. #include "netlink.h"
  4. #include "common.h"
  5. #include "bitset.h"
  6. struct tsinfo_req_info {
  7. struct ethnl_req_info base;
  8. };
  9. struct tsinfo_reply_data {
  10. struct ethnl_reply_data base;
  11. struct kernel_ethtool_ts_info ts_info;
  12. struct ethtool_ts_stats stats;
  13. };
  14. #define TSINFO_REPDATA(__reply_base) \
  15. container_of(__reply_base, struct tsinfo_reply_data, base)
  16. #define ETHTOOL_TS_STAT_CNT \
  17. (__ETHTOOL_A_TS_STAT_CNT - (ETHTOOL_A_TS_STAT_UNSPEC + 1))
  18. const struct nla_policy ethnl_tsinfo_get_policy[] = {
  19. [ETHTOOL_A_TSINFO_HEADER] =
  20. NLA_POLICY_NESTED(ethnl_header_policy_stats),
  21. };
  22. static int tsinfo_prepare_data(const struct ethnl_req_info *req_base,
  23. struct ethnl_reply_data *reply_base,
  24. const struct genl_info *info)
  25. {
  26. struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
  27. struct net_device *dev = reply_base->dev;
  28. int ret;
  29. ret = ethnl_ops_begin(dev);
  30. if (ret < 0)
  31. return ret;
  32. if (req_base->flags & ETHTOOL_FLAG_STATS) {
  33. ethtool_stats_init((u64 *)&data->stats,
  34. sizeof(data->stats) / sizeof(u64));
  35. if (dev->ethtool_ops->get_ts_stats)
  36. dev->ethtool_ops->get_ts_stats(dev, &data->stats);
  37. }
  38. ret = __ethtool_get_ts_info(dev, &data->ts_info);
  39. ethnl_ops_complete(dev);
  40. return ret;
  41. }
  42. static int tsinfo_reply_size(const struct ethnl_req_info *req_base,
  43. const struct ethnl_reply_data *reply_base)
  44. {
  45. const struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
  46. bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
  47. const struct kernel_ethtool_ts_info *ts_info = &data->ts_info;
  48. int len = 0;
  49. int ret;
  50. BUILD_BUG_ON(__SOF_TIMESTAMPING_CNT > 32);
  51. BUILD_BUG_ON(__HWTSTAMP_TX_CNT > 32);
  52. BUILD_BUG_ON(__HWTSTAMP_FILTER_CNT > 32);
  53. if (ts_info->so_timestamping) {
  54. ret = ethnl_bitset32_size(&ts_info->so_timestamping, NULL,
  55. __SOF_TIMESTAMPING_CNT,
  56. sof_timestamping_names, compact);
  57. if (ret < 0)
  58. return ret;
  59. len += ret; /* _TSINFO_TIMESTAMPING */
  60. }
  61. if (ts_info->tx_types) {
  62. ret = ethnl_bitset32_size(&ts_info->tx_types, NULL,
  63. __HWTSTAMP_TX_CNT,
  64. ts_tx_type_names, compact);
  65. if (ret < 0)
  66. return ret;
  67. len += ret; /* _TSINFO_TX_TYPES */
  68. }
  69. if (ts_info->rx_filters) {
  70. ret = ethnl_bitset32_size(&ts_info->rx_filters, NULL,
  71. __HWTSTAMP_FILTER_CNT,
  72. ts_rx_filter_names, compact);
  73. if (ret < 0)
  74. return ret;
  75. len += ret; /* _TSINFO_RX_FILTERS */
  76. }
  77. if (ts_info->phc_index >= 0)
  78. len += nla_total_size(sizeof(u32)); /* _TSINFO_PHC_INDEX */
  79. if (req_base->flags & ETHTOOL_FLAG_STATS)
  80. len += nla_total_size(0) + /* _TSINFO_STATS */
  81. nla_total_size_64bit(sizeof(u64)) * ETHTOOL_TS_STAT_CNT;
  82. return len;
  83. }
  84. static int tsinfo_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)
  85. {
  86. if (val == ETHTOOL_STAT_NOT_SET)
  87. return 0;
  88. if (nla_put_uint(skb, attrtype, val))
  89. return -EMSGSIZE;
  90. return 0;
  91. }
  92. static int tsinfo_put_stats(struct sk_buff *skb,
  93. const struct ethtool_ts_stats *stats)
  94. {
  95. struct nlattr *nest;
  96. nest = nla_nest_start(skb, ETHTOOL_A_TSINFO_STATS);
  97. if (!nest)
  98. return -EMSGSIZE;
  99. if (tsinfo_put_stat(skb, stats->tx_stats.pkts,
  100. ETHTOOL_A_TS_STAT_TX_PKTS) ||
  101. tsinfo_put_stat(skb, stats->tx_stats.lost,
  102. ETHTOOL_A_TS_STAT_TX_LOST) ||
  103. tsinfo_put_stat(skb, stats->tx_stats.err,
  104. ETHTOOL_A_TS_STAT_TX_ERR))
  105. goto err_cancel;
  106. nla_nest_end(skb, nest);
  107. return 0;
  108. err_cancel:
  109. nla_nest_cancel(skb, nest);
  110. return -EMSGSIZE;
  111. }
  112. static int tsinfo_fill_reply(struct sk_buff *skb,
  113. const struct ethnl_req_info *req_base,
  114. const struct ethnl_reply_data *reply_base)
  115. {
  116. const struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base);
  117. bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
  118. const struct kernel_ethtool_ts_info *ts_info = &data->ts_info;
  119. int ret;
  120. if (ts_info->so_timestamping) {
  121. ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_TIMESTAMPING,
  122. &ts_info->so_timestamping, NULL,
  123. __SOF_TIMESTAMPING_CNT,
  124. sof_timestamping_names, compact);
  125. if (ret < 0)
  126. return ret;
  127. }
  128. if (ts_info->tx_types) {
  129. ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_TX_TYPES,
  130. &ts_info->tx_types, NULL,
  131. __HWTSTAMP_TX_CNT,
  132. ts_tx_type_names, compact);
  133. if (ret < 0)
  134. return ret;
  135. }
  136. if (ts_info->rx_filters) {
  137. ret = ethnl_put_bitset32(skb, ETHTOOL_A_TSINFO_RX_FILTERS,
  138. &ts_info->rx_filters, NULL,
  139. __HWTSTAMP_FILTER_CNT,
  140. ts_rx_filter_names, compact);
  141. if (ret < 0)
  142. return ret;
  143. }
  144. if (ts_info->phc_index >= 0 &&
  145. nla_put_u32(skb, ETHTOOL_A_TSINFO_PHC_INDEX, ts_info->phc_index))
  146. return -EMSGSIZE;
  147. if (req_base->flags & ETHTOOL_FLAG_STATS &&
  148. tsinfo_put_stats(skb, &data->stats))
  149. return -EMSGSIZE;
  150. return 0;
  151. }
  152. const struct ethnl_request_ops ethnl_tsinfo_request_ops = {
  153. .request_cmd = ETHTOOL_MSG_TSINFO_GET,
  154. .reply_cmd = ETHTOOL_MSG_TSINFO_GET_REPLY,
  155. .hdr_attr = ETHTOOL_A_TSINFO_HEADER,
  156. .req_info_size = sizeof(struct tsinfo_req_info),
  157. .reply_data_size = sizeof(struct tsinfo_reply_data),
  158. .prepare_data = tsinfo_prepare_data,
  159. .reply_size = tsinfo_reply_size,
  160. .fill_reply = tsinfo_fill_reply,
  161. };