mse.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <linux/ethtool.h>
  3. #include <linux/phy.h>
  4. #include <linux/slab.h>
  5. #include "netlink.h"
  6. #include "common.h"
  7. /* Channels A-D only; WORST and LINK are exclusive alternatives */
  8. #define PHY_MSE_CHANNEL_COUNT 4
  9. struct mse_req_info {
  10. struct ethnl_req_info base;
  11. };
  12. struct mse_snapshot_entry {
  13. struct phy_mse_snapshot snapshot;
  14. int channel;
  15. };
  16. struct mse_reply_data {
  17. struct ethnl_reply_data base;
  18. struct phy_mse_capability capability;
  19. struct mse_snapshot_entry *snapshots;
  20. unsigned int num_snapshots;
  21. };
  22. static struct mse_reply_data *
  23. mse_repdata(const struct ethnl_reply_data *reply_base)
  24. {
  25. return container_of(reply_base, struct mse_reply_data, base);
  26. }
  27. const struct nla_policy ethnl_mse_get_policy[] = {
  28. [ETHTOOL_A_MSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_phy),
  29. };
  30. static int get_snapshot_if_supported(struct phy_device *phydev,
  31. struct mse_reply_data *data,
  32. unsigned int *idx, u32 cap_bit,
  33. enum phy_mse_channel channel)
  34. {
  35. int ret;
  36. if (data->capability.supported_caps & cap_bit) {
  37. ret = phydev->drv->get_mse_snapshot(phydev, channel,
  38. &data->snapshots[*idx].snapshot);
  39. if (ret)
  40. return ret;
  41. data->snapshots[*idx].channel = channel;
  42. (*idx)++;
  43. }
  44. return 0;
  45. }
  46. static int mse_get_channels(struct phy_device *phydev,
  47. struct mse_reply_data *data)
  48. {
  49. unsigned int i = 0;
  50. int ret;
  51. if (!data->capability.supported_caps)
  52. return 0;
  53. data->snapshots = kzalloc_objs(*data->snapshots, PHY_MSE_CHANNEL_COUNT);
  54. if (!data->snapshots)
  55. return -ENOMEM;
  56. /* Priority 1: Individual channels */
  57. ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_A,
  58. PHY_MSE_CHANNEL_A);
  59. if (ret)
  60. return ret;
  61. ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_B,
  62. PHY_MSE_CHANNEL_B);
  63. if (ret)
  64. return ret;
  65. ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_C,
  66. PHY_MSE_CHANNEL_C);
  67. if (ret)
  68. return ret;
  69. ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_D,
  70. PHY_MSE_CHANNEL_D);
  71. if (ret)
  72. return ret;
  73. /* If any individual channels were found, we are done. */
  74. if (i > 0) {
  75. data->num_snapshots = i;
  76. return 0;
  77. }
  78. /* Priority 2: Worst channel, if no individual channels supported. */
  79. ret = get_snapshot_if_supported(phydev, data, &i,
  80. PHY_MSE_CAP_WORST_CHANNEL,
  81. PHY_MSE_CHANNEL_WORST);
  82. if (ret)
  83. return ret;
  84. /* If worst channel was found, we are done. */
  85. if (i > 0) {
  86. data->num_snapshots = i;
  87. return 0;
  88. }
  89. /* Priority 3: Link-wide, if nothing else is supported. */
  90. ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_LINK,
  91. PHY_MSE_CHANNEL_LINK);
  92. if (ret)
  93. return ret;
  94. data->num_snapshots = i;
  95. return 0;
  96. }
  97. static int mse_prepare_data(const struct ethnl_req_info *req_base,
  98. struct ethnl_reply_data *reply_base,
  99. const struct genl_info *info)
  100. {
  101. struct mse_reply_data *data = mse_repdata(reply_base);
  102. struct net_device *dev = reply_base->dev;
  103. struct phy_device *phydev;
  104. int ret;
  105. phydev = ethnl_req_get_phydev(req_base, info->attrs,
  106. ETHTOOL_A_MSE_HEADER, info->extack);
  107. if (IS_ERR(phydev))
  108. return PTR_ERR(phydev);
  109. if (!phydev)
  110. return -EOPNOTSUPP;
  111. ret = ethnl_ops_begin(dev);
  112. if (ret)
  113. return ret;
  114. mutex_lock(&phydev->lock);
  115. if (!phydev->drv || !phydev->drv->get_mse_capability ||
  116. !phydev->drv->get_mse_snapshot) {
  117. ret = -EOPNOTSUPP;
  118. goto out_unlock;
  119. }
  120. if (!phydev->link) {
  121. ret = -ENETDOWN;
  122. goto out_unlock;
  123. }
  124. ret = phydev->drv->get_mse_capability(phydev, &data->capability);
  125. if (ret)
  126. goto out_unlock;
  127. ret = mse_get_channels(phydev, data);
  128. out_unlock:
  129. mutex_unlock(&phydev->lock);
  130. ethnl_ops_complete(dev);
  131. if (ret)
  132. kfree(data->snapshots);
  133. return ret;
  134. }
  135. static void mse_cleanup_data(struct ethnl_reply_data *reply_base)
  136. {
  137. struct mse_reply_data *data = mse_repdata(reply_base);
  138. kfree(data->snapshots);
  139. }
  140. static int mse_reply_size(const struct ethnl_req_info *req_base,
  141. const struct ethnl_reply_data *reply_base)
  142. {
  143. const struct mse_reply_data *data = mse_repdata(reply_base);
  144. size_t len = 0;
  145. unsigned int i;
  146. /* ETHTOOL_A_MSE_CAPABILITIES */
  147. len += nla_total_size(0);
  148. if (data->capability.supported_caps & PHY_MSE_CAP_AVG)
  149. /* ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE */
  150. len += nla_total_size(sizeof(u64));
  151. if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |
  152. PHY_MSE_CAP_WORST_PEAK))
  153. /* ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE */
  154. len += nla_total_size(sizeof(u64));
  155. /* ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS */
  156. len += nla_total_size(sizeof(u64));
  157. /* ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS */
  158. len += nla_total_size(sizeof(u64));
  159. for (i = 0; i < data->num_snapshots; i++) {
  160. size_t snapshot_len = 0;
  161. /* Per-channel nest (e.g., ETHTOOL_A_MSE_CHANNEL_A / _B / _C /
  162. * _D / _WORST_CHANNEL / _LINK)
  163. */
  164. snapshot_len += nla_total_size(0);
  165. if (data->capability.supported_caps & PHY_MSE_CAP_AVG)
  166. snapshot_len += nla_total_size(sizeof(u64));
  167. if (data->capability.supported_caps & PHY_MSE_CAP_PEAK)
  168. snapshot_len += nla_total_size(sizeof(u64));
  169. if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK)
  170. snapshot_len += nla_total_size(sizeof(u64));
  171. len += snapshot_len;
  172. }
  173. return len;
  174. }
  175. static int mse_channel_to_attr(int ch)
  176. {
  177. switch (ch) {
  178. case PHY_MSE_CHANNEL_A:
  179. return ETHTOOL_A_MSE_CHANNEL_A;
  180. case PHY_MSE_CHANNEL_B:
  181. return ETHTOOL_A_MSE_CHANNEL_B;
  182. case PHY_MSE_CHANNEL_C:
  183. return ETHTOOL_A_MSE_CHANNEL_C;
  184. case PHY_MSE_CHANNEL_D:
  185. return ETHTOOL_A_MSE_CHANNEL_D;
  186. case PHY_MSE_CHANNEL_WORST:
  187. return ETHTOOL_A_MSE_WORST_CHANNEL;
  188. case PHY_MSE_CHANNEL_LINK:
  189. return ETHTOOL_A_MSE_LINK;
  190. default:
  191. return -EINVAL;
  192. }
  193. }
  194. static int mse_fill_reply(struct sk_buff *skb,
  195. const struct ethnl_req_info *req_base,
  196. const struct ethnl_reply_data *reply_base)
  197. {
  198. const struct mse_reply_data *data = mse_repdata(reply_base);
  199. struct nlattr *nest;
  200. unsigned int i;
  201. int ret;
  202. nest = nla_nest_start(skb, ETHTOOL_A_MSE_CAPABILITIES);
  203. if (!nest)
  204. return -EMSGSIZE;
  205. if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {
  206. ret = nla_put_uint(skb,
  207. ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE,
  208. data->capability.max_average_mse);
  209. if (ret < 0)
  210. goto nla_put_nest_failure;
  211. }
  212. if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |
  213. PHY_MSE_CAP_WORST_PEAK)) {
  214. ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE,
  215. data->capability.max_peak_mse);
  216. if (ret < 0)
  217. goto nla_put_nest_failure;
  218. }
  219. ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS,
  220. data->capability.refresh_rate_ps);
  221. if (ret < 0)
  222. goto nla_put_nest_failure;
  223. ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS,
  224. data->capability.num_symbols);
  225. if (ret < 0)
  226. goto nla_put_nest_failure;
  227. nla_nest_end(skb, nest);
  228. for (i = 0; i < data->num_snapshots; i++) {
  229. const struct mse_snapshot_entry *s = &data->snapshots[i];
  230. int chan_attr;
  231. chan_attr = mse_channel_to_attr(s->channel);
  232. if (chan_attr < 0)
  233. return chan_attr;
  234. nest = nla_nest_start(skb, chan_attr);
  235. if (!nest)
  236. return -EMSGSIZE;
  237. if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {
  238. ret = nla_put_uint(skb,
  239. ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE,
  240. s->snapshot.average_mse);
  241. if (ret)
  242. goto nla_put_nest_failure;
  243. }
  244. if (data->capability.supported_caps & PHY_MSE_CAP_PEAK) {
  245. ret = nla_put_uint(skb, ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE,
  246. s->snapshot.peak_mse);
  247. if (ret)
  248. goto nla_put_nest_failure;
  249. }
  250. if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK) {
  251. ret = nla_put_uint(skb,
  252. ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE,
  253. s->snapshot.worst_peak_mse);
  254. if (ret)
  255. goto nla_put_nest_failure;
  256. }
  257. nla_nest_end(skb, nest);
  258. }
  259. return 0;
  260. nla_put_nest_failure:
  261. nla_nest_cancel(skb, nest);
  262. return ret;
  263. }
  264. const struct ethnl_request_ops ethnl_mse_request_ops = {
  265. .request_cmd = ETHTOOL_MSG_MSE_GET,
  266. .reply_cmd = ETHTOOL_MSG_MSE_GET_REPLY,
  267. .hdr_attr = ETHTOOL_A_MSE_HEADER,
  268. .req_info_size = sizeof(struct mse_req_info),
  269. .reply_data_size = sizeof(struct mse_reply_data),
  270. .prepare_data = mse_prepare_data,
  271. .cleanup_data = mse_cleanup_data,
  272. .reply_size = mse_reply_size,
  273. .fill_reply = mse_fill_reply,
  274. };