coalesce.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <linux/dim.h>
  3. #include "netlink.h"
  4. #include "common.h"
  5. struct coalesce_req_info {
  6. struct ethnl_req_info base;
  7. };
  8. struct coalesce_reply_data {
  9. struct ethnl_reply_data base;
  10. struct ethtool_coalesce coalesce;
  11. struct kernel_ethtool_coalesce kernel_coalesce;
  12. u32 supported_params;
  13. };
  14. #define COALESCE_REPDATA(__reply_base) \
  15. container_of(__reply_base, struct coalesce_reply_data, base)
  16. #define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS
  17. static u32 attr_to_mask(unsigned int attr_type)
  18. {
  19. return BIT(attr_type - __SUPPORTED_OFFSET);
  20. }
  21. /* build time check that indices in ethtool_ops::supported_coalesce_params
  22. * match corresponding attribute types with an offset
  23. */
  24. #define __CHECK_SUPPORTED_OFFSET(x) \
  25. static_assert((ETHTOOL_ ## x) == \
  26. BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET))
  27. __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS);
  28. __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES);
  29. __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ);
  30. __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ);
  31. __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS);
  32. __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES);
  33. __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ);
  34. __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ);
  35. __CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS);
  36. __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX);
  37. __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX);
  38. __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW);
  39. __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW);
  40. __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW);
  41. __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW);
  42. __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW);
  43. __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH);
  44. __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH);
  45. __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH);
  46. __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH);
  47. __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH);
  48. __CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL);
  49. const struct nla_policy ethnl_coalesce_get_policy[] = {
  50. [ETHTOOL_A_COALESCE_HEADER] =
  51. NLA_POLICY_NESTED(ethnl_header_policy),
  52. };
  53. static int coalesce_prepare_data(const struct ethnl_req_info *req_base,
  54. struct ethnl_reply_data *reply_base,
  55. const struct genl_info *info)
  56. {
  57. struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
  58. struct net_device *dev = reply_base->dev;
  59. int ret;
  60. if (!dev->ethtool_ops->get_coalesce)
  61. return -EOPNOTSUPP;
  62. data->supported_params = dev->ethtool_ops->supported_coalesce_params;
  63. ret = ethnl_ops_begin(dev);
  64. if (ret < 0)
  65. return ret;
  66. ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce,
  67. &data->kernel_coalesce,
  68. info->extack);
  69. ethnl_ops_complete(dev);
  70. return ret;
  71. }
  72. static int coalesce_reply_size(const struct ethnl_req_info *req_base,
  73. const struct ethnl_reply_data *reply_base)
  74. {
  75. int modersz = nla_total_size(0) + /* _PROFILE_IRQ_MODERATION, nest */
  76. nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_USEC */
  77. nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_PKTS */
  78. nla_total_size(sizeof(u32)); /* _IRQ_MODERATION_COMPS */
  79. int total_modersz = nla_total_size(0) + /* _{R,T}X_PROFILE, nest */
  80. modersz * NET_DIM_PARAMS_NUM_PROFILES;
  81. return nla_total_size(sizeof(u32)) + /* _RX_USECS */
  82. nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES */
  83. nla_total_size(sizeof(u32)) + /* _RX_USECS_IRQ */
  84. nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_IRQ */
  85. nla_total_size(sizeof(u32)) + /* _TX_USECS */
  86. nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES */
  87. nla_total_size(sizeof(u32)) + /* _TX_USECS_IRQ */
  88. nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_IRQ */
  89. nla_total_size(sizeof(u32)) + /* _STATS_BLOCK_USECS */
  90. nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_RX */
  91. nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_TX */
  92. nla_total_size(sizeof(u32)) + /* _PKT_RATE_LOW */
  93. nla_total_size(sizeof(u32)) + /* _RX_USECS_LOW */
  94. nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_LOW */
  95. nla_total_size(sizeof(u32)) + /* _TX_USECS_LOW */
  96. nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_LOW */
  97. nla_total_size(sizeof(u32)) + /* _PKT_RATE_HIGH */
  98. nla_total_size(sizeof(u32)) + /* _RX_USECS_HIGH */
  99. nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */
  100. nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */
  101. nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */
  102. nla_total_size(sizeof(u32)) + /* _RATE_SAMPLE_INTERVAL */
  103. nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_TX */
  104. nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_RX */
  105. nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_BYTES */
  106. nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_FRAMES */
  107. nla_total_size(sizeof(u32)) + /* _TX_AGGR_TIME_USECS */
  108. total_modersz * 2; /* _{R,T}X_PROFILE */
  109. }
  110. static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
  111. u32 supported_params)
  112. {
  113. if (!val && !(supported_params & attr_to_mask(attr_type)))
  114. return false;
  115. return nla_put_u32(skb, attr_type, val);
  116. }
  117. static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val,
  118. u32 supported_params)
  119. {
  120. if (!val && !(supported_params & attr_to_mask(attr_type)))
  121. return false;
  122. return nla_put_u8(skb, attr_type, !!val);
  123. }
  124. /**
  125. * coalesce_put_profile - fill reply with a nla nest with four child nla nests.
  126. * @skb: socket buffer the message is stored in
  127. * @attr_type: nest attr type ETHTOOL_A_COALESCE_*X_PROFILE
  128. * @profile: data passed to userspace
  129. * @coal_flags: modifiable parameters supported by the driver
  130. *
  131. * Put a dim profile nest attribute. Refer to ETHTOOL_A_PROFILE_IRQ_MODERATION.
  132. *
  133. * Return: 0 on success or a negative error code.
  134. */
  135. static int coalesce_put_profile(struct sk_buff *skb, u16 attr_type,
  136. const struct dim_cq_moder *profile,
  137. u8 coal_flags)
  138. {
  139. struct nlattr *profile_attr, *moder_attr;
  140. int i, ret;
  141. if (!profile || !coal_flags)
  142. return 0;
  143. profile_attr = nla_nest_start(skb, attr_type);
  144. if (!profile_attr)
  145. return -EMSGSIZE;
  146. for (i = 0; i < NET_DIM_PARAMS_NUM_PROFILES; i++) {
  147. moder_attr = nla_nest_start(skb,
  148. ETHTOOL_A_PROFILE_IRQ_MODERATION);
  149. if (!moder_attr) {
  150. ret = -EMSGSIZE;
  151. goto cancel_profile;
  152. }
  153. if (coal_flags & DIM_COALESCE_USEC) {
  154. ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_USEC,
  155. profile[i].usec);
  156. if (ret)
  157. goto cancel_moder;
  158. }
  159. if (coal_flags & DIM_COALESCE_PKTS) {
  160. ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_PKTS,
  161. profile[i].pkts);
  162. if (ret)
  163. goto cancel_moder;
  164. }
  165. if (coal_flags & DIM_COALESCE_COMPS) {
  166. ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_COMPS,
  167. profile[i].comps);
  168. if (ret)
  169. goto cancel_moder;
  170. }
  171. nla_nest_end(skb, moder_attr);
  172. }
  173. nla_nest_end(skb, profile_attr);
  174. return 0;
  175. cancel_moder:
  176. nla_nest_cancel(skb, moder_attr);
  177. cancel_profile:
  178. nla_nest_cancel(skb, profile_attr);
  179. return ret;
  180. }
  181. static int coalesce_fill_reply(struct sk_buff *skb,
  182. const struct ethnl_req_info *req_base,
  183. const struct ethnl_reply_data *reply_base)
  184. {
  185. const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
  186. const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce;
  187. const struct ethtool_coalesce *coal = &data->coalesce;
  188. u32 supported = data->supported_params;
  189. struct dim_irq_moder *moder;
  190. int ret = 0;
  191. if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS,
  192. coal->rx_coalesce_usecs, supported) ||
  193. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
  194. coal->rx_max_coalesced_frames, supported) ||
  195. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ,
  196. coal->rx_coalesce_usecs_irq, supported) ||
  197. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
  198. coal->rx_max_coalesced_frames_irq, supported) ||
  199. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS,
  200. coal->tx_coalesce_usecs, supported) ||
  201. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
  202. coal->tx_max_coalesced_frames, supported) ||
  203. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ,
  204. coal->tx_coalesce_usecs_irq, supported) ||
  205. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
  206. coal->tx_max_coalesced_frames_irq, supported) ||
  207. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
  208. coal->stats_block_coalesce_usecs, supported) ||
  209. coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
  210. coal->use_adaptive_rx_coalesce, supported) ||
  211. coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
  212. coal->use_adaptive_tx_coalesce, supported) ||
  213. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW,
  214. coal->pkt_rate_low, supported) ||
  215. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW,
  216. coal->rx_coalesce_usecs_low, supported) ||
  217. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
  218. coal->rx_max_coalesced_frames_low, supported) ||
  219. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW,
  220. coal->tx_coalesce_usecs_low, supported) ||
  221. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
  222. coal->tx_max_coalesced_frames_low, supported) ||
  223. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
  224. coal->pkt_rate_high, supported) ||
  225. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH,
  226. coal->rx_coalesce_usecs_high, supported) ||
  227. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
  228. coal->rx_max_coalesced_frames_high, supported) ||
  229. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH,
  230. coal->tx_coalesce_usecs_high, supported) ||
  231. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
  232. coal->tx_max_coalesced_frames_high, supported) ||
  233. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
  234. coal->rate_sample_interval, supported) ||
  235. coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX,
  236. kcoal->use_cqe_mode_tx, supported) ||
  237. coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX,
  238. kcoal->use_cqe_mode_rx, supported) ||
  239. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES,
  240. kcoal->tx_aggr_max_bytes, supported) ||
  241. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES,
  242. kcoal->tx_aggr_max_frames, supported) ||
  243. coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS,
  244. kcoal->tx_aggr_time_usecs, supported))
  245. return -EMSGSIZE;
  246. if (!req_base->dev || !req_base->dev->irq_moder)
  247. return 0;
  248. moder = req_base->dev->irq_moder;
  249. rcu_read_lock();
  250. if (moder->profile_flags & DIM_PROFILE_RX) {
  251. ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_RX_PROFILE,
  252. rcu_dereference(moder->rx_profile),
  253. moder->coal_flags);
  254. if (ret)
  255. goto out;
  256. }
  257. if (moder->profile_flags & DIM_PROFILE_TX)
  258. ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_TX_PROFILE,
  259. rcu_dereference(moder->tx_profile),
  260. moder->coal_flags);
  261. out:
  262. rcu_read_unlock();
  263. return ret;
  264. }
  265. /* COALESCE_SET */
  266. static const struct nla_policy coalesce_irq_moderation_policy[] = {
  267. [ETHTOOL_A_IRQ_MODERATION_USEC] = { .type = NLA_U32 },
  268. [ETHTOOL_A_IRQ_MODERATION_PKTS] = { .type = NLA_U32 },
  269. [ETHTOOL_A_IRQ_MODERATION_COMPS] = { .type = NLA_U32 },
  270. };
  271. static const struct nla_policy coalesce_profile_policy[] = {
  272. [ETHTOOL_A_PROFILE_IRQ_MODERATION] =
  273. NLA_POLICY_NESTED(coalesce_irq_moderation_policy),
  274. };
  275. const struct nla_policy ethnl_coalesce_set_policy[] = {
  276. [ETHTOOL_A_COALESCE_HEADER] =
  277. NLA_POLICY_NESTED(ethnl_header_policy),
  278. [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 },
  279. [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 },
  280. [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 },
  281. [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 },
  282. [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 },
  283. [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 },
  284. [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 },
  285. [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 },
  286. [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 },
  287. [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 },
  288. [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 },
  289. [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 },
  290. [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 },
  291. [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 },
  292. [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 },
  293. [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 },
  294. [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 },
  295. [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 },
  296. [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
  297. [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 },
  298. [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
  299. [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 },
  300. [ETHTOOL_A_COALESCE_USE_CQE_MODE_TX] = NLA_POLICY_MAX(NLA_U8, 1),
  301. [ETHTOOL_A_COALESCE_USE_CQE_MODE_RX] = NLA_POLICY_MAX(NLA_U8, 1),
  302. [ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 },
  303. [ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 },
  304. [ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 },
  305. [ETHTOOL_A_COALESCE_RX_PROFILE] =
  306. NLA_POLICY_NESTED(coalesce_profile_policy),
  307. [ETHTOOL_A_COALESCE_TX_PROFILE] =
  308. NLA_POLICY_NESTED(coalesce_profile_policy),
  309. };
  310. static int
  311. ethnl_set_coalesce_validate(struct ethnl_req_info *req_info,
  312. struct genl_info *info)
  313. {
  314. const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
  315. struct dim_irq_moder *irq_moder = req_info->dev->irq_moder;
  316. struct nlattr **tb = info->attrs;
  317. u32 supported_params;
  318. u16 a;
  319. if (!ops->get_coalesce || !ops->set_coalesce)
  320. return -EOPNOTSUPP;
  321. /* make sure that only supported parameters are present */
  322. supported_params = ops->supported_coalesce_params;
  323. if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_RX)
  324. supported_params |= ETHTOOL_COALESCE_RX_PROFILE;
  325. if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_TX)
  326. supported_params |= ETHTOOL_COALESCE_TX_PROFILE;
  327. for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++)
  328. if (tb[a] && !(supported_params & attr_to_mask(a))) {
  329. NL_SET_ERR_MSG_ATTR(info->extack, tb[a],
  330. "cannot modify an unsupported parameter");
  331. return -EINVAL;
  332. }
  333. return 1;
  334. }
  335. /**
  336. * ethnl_update_irq_moder - update a specific field in the given profile
  337. * @irq_moder: place that collects dim related information
  338. * @irq_field: field in profile to modify
  339. * @attr_type: attr type ETHTOOL_A_IRQ_MODERATION_*
  340. * @tb: netlink attribute with new values or null
  341. * @coal_bit: DIM_COALESCE_* bit from coal_flags
  342. * @mod: pointer to bool for modification tracking
  343. * @extack: netlink extended ack
  344. *
  345. * Return: 0 on success or a negative error code.
  346. */
  347. static int ethnl_update_irq_moder(struct dim_irq_moder *irq_moder,
  348. u16 *irq_field, u16 attr_type,
  349. struct nlattr **tb,
  350. u8 coal_bit, bool *mod,
  351. struct netlink_ext_ack *extack)
  352. {
  353. int ret = 0;
  354. u32 val;
  355. if (!tb[attr_type])
  356. return 0;
  357. if (irq_moder->coal_flags & coal_bit) {
  358. val = nla_get_u32(tb[attr_type]);
  359. if (*irq_field == val)
  360. return 0;
  361. *irq_field = val;
  362. *mod = true;
  363. } else {
  364. NL_SET_BAD_ATTR(extack, tb[attr_type]);
  365. ret = -EOPNOTSUPP;
  366. }
  367. return ret;
  368. }
  369. /**
  370. * ethnl_update_profile - get a profile nest with child nests from userspace.
  371. * @dev: netdevice to update the profile
  372. * @dst: profile get from the driver and modified by ethnl_update_profile.
  373. * @nests: nest attr ETHTOOL_A_COALESCE_*X_PROFILE to set profile.
  374. * @mod: pointer to bool for modification tracking
  375. * @extack: Netlink extended ack
  376. *
  377. * Layout of nests:
  378. * Nested ETHTOOL_A_COALESCE_*X_PROFILE attr
  379. * Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr
  380. * ETHTOOL_A_IRQ_MODERATION_USEC attr
  381. * ETHTOOL_A_IRQ_MODERATION_PKTS attr
  382. * ETHTOOL_A_IRQ_MODERATION_COMPS attr
  383. * ...
  384. * Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr
  385. * ETHTOOL_A_IRQ_MODERATION_USEC attr
  386. * ETHTOOL_A_IRQ_MODERATION_PKTS attr
  387. * ETHTOOL_A_IRQ_MODERATION_COMPS attr
  388. *
  389. * Return: 0 on success or a negative error code.
  390. */
  391. static int ethnl_update_profile(struct net_device *dev,
  392. struct dim_cq_moder __rcu **dst,
  393. const struct nlattr *nests,
  394. bool *mod,
  395. struct netlink_ext_ack *extack)
  396. {
  397. int len_irq_moder = ARRAY_SIZE(coalesce_irq_moderation_policy);
  398. struct nlattr *tb[ARRAY_SIZE(coalesce_irq_moderation_policy)];
  399. struct dim_irq_moder *irq_moder = dev->irq_moder;
  400. struct dim_cq_moder *new_profile, *old_profile;
  401. int ret, rem, i = 0, len;
  402. struct nlattr *nest;
  403. if (!nests)
  404. return 0;
  405. if (!*dst)
  406. return -EOPNOTSUPP;
  407. old_profile = rtnl_dereference(*dst);
  408. len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*old_profile);
  409. new_profile = kmemdup(old_profile, len, GFP_KERNEL);
  410. if (!new_profile)
  411. return -ENOMEM;
  412. nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION,
  413. nests, rem) {
  414. ret = nla_parse_nested(tb, len_irq_moder - 1, nest,
  415. coalesce_irq_moderation_policy,
  416. extack);
  417. if (ret)
  418. goto err_out;
  419. ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].usec,
  420. ETHTOOL_A_IRQ_MODERATION_USEC,
  421. tb, DIM_COALESCE_USEC,
  422. mod, extack);
  423. if (ret)
  424. goto err_out;
  425. ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].pkts,
  426. ETHTOOL_A_IRQ_MODERATION_PKTS,
  427. tb, DIM_COALESCE_PKTS,
  428. mod, extack);
  429. if (ret)
  430. goto err_out;
  431. ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].comps,
  432. ETHTOOL_A_IRQ_MODERATION_COMPS,
  433. tb, DIM_COALESCE_COMPS,
  434. mod, extack);
  435. if (ret)
  436. goto err_out;
  437. i++;
  438. }
  439. /* After the profile is modified, dim itself is a dynamic
  440. * mechanism and will quickly fit to the appropriate
  441. * coalescing parameters according to the new profile.
  442. */
  443. rcu_assign_pointer(*dst, new_profile);
  444. kfree_rcu(old_profile, rcu);
  445. return 0;
  446. err_out:
  447. kfree(new_profile);
  448. return ret;
  449. }
  450. static int
  451. __ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info,
  452. bool *dual_change)
  453. {
  454. struct kernel_ethtool_coalesce kernel_coalesce = {};
  455. struct net_device *dev = req_info->dev;
  456. struct ethtool_coalesce coalesce = {};
  457. bool mod_mode = false, mod = false;
  458. struct nlattr **tb = info->attrs;
  459. int ret;
  460. ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce,
  461. info->extack);
  462. if (ret < 0)
  463. return ret;
  464. /* Update values */
  465. ethnl_update_u32(&coalesce.rx_coalesce_usecs,
  466. tb[ETHTOOL_A_COALESCE_RX_USECS], &mod);
  467. ethnl_update_u32(&coalesce.rx_max_coalesced_frames,
  468. tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod);
  469. ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq,
  470. tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod);
  471. ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq,
  472. tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod);
  473. ethnl_update_u32(&coalesce.tx_coalesce_usecs,
  474. tb[ETHTOOL_A_COALESCE_TX_USECS], &mod);
  475. ethnl_update_u32(&coalesce.tx_max_coalesced_frames,
  476. tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod);
  477. ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq,
  478. tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod);
  479. ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq,
  480. tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod);
  481. ethnl_update_u32(&coalesce.stats_block_coalesce_usecs,
  482. tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod);
  483. ethnl_update_u32(&coalesce.pkt_rate_low,
  484. tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod);
  485. ethnl_update_u32(&coalesce.rx_coalesce_usecs_low,
  486. tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod);
  487. ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low,
  488. tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod);
  489. ethnl_update_u32(&coalesce.tx_coalesce_usecs_low,
  490. tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod);
  491. ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low,
  492. tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod);
  493. ethnl_update_u32(&coalesce.pkt_rate_high,
  494. tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod);
  495. ethnl_update_u32(&coalesce.rx_coalesce_usecs_high,
  496. tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod);
  497. ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high,
  498. tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod);
  499. ethnl_update_u32(&coalesce.tx_coalesce_usecs_high,
  500. tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod);
  501. ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high,
  502. tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod);
  503. ethnl_update_u32(&coalesce.rate_sample_interval,
  504. tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod);
  505. ethnl_update_u32(&kernel_coalesce.tx_aggr_max_bytes,
  506. tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], &mod);
  507. ethnl_update_u32(&kernel_coalesce.tx_aggr_max_frames,
  508. tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod);
  509. ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs,
  510. tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod);
  511. if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_RX) {
  512. ret = ethnl_update_profile(dev, &dev->irq_moder->rx_profile,
  513. tb[ETHTOOL_A_COALESCE_RX_PROFILE],
  514. &mod, info->extack);
  515. if (ret < 0)
  516. return ret;
  517. }
  518. if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_TX) {
  519. ret = ethnl_update_profile(dev, &dev->irq_moder->tx_profile,
  520. tb[ETHTOOL_A_COALESCE_TX_PROFILE],
  521. &mod, info->extack);
  522. if (ret < 0)
  523. return ret;
  524. }
  525. /* Update operation modes */
  526. ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce,
  527. tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode);
  528. ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce,
  529. tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod_mode);
  530. ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx,
  531. tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod_mode);
  532. ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx,
  533. tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod_mode);
  534. *dual_change = mod && mod_mode;
  535. if (!mod && !mod_mode)
  536. return 0;
  537. ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce,
  538. info->extack);
  539. return ret < 0 ? ret : 1;
  540. }
  541. static int
  542. ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info)
  543. {
  544. bool dual_change;
  545. int err, ret;
  546. /* SET_COALESCE may change operation mode and parameters in one call.
  547. * Changing operation mode may cause the driver to reset the parameter
  548. * values, and therefore ignore user input (driver does not know which
  549. * parameters come from user and which are echoed back from ->get).
  550. * To not complicate the drivers if user tries to change both the mode
  551. * and parameters at once - call the driver twice.
  552. */
  553. err = __ethnl_set_coalesce(req_info, info, &dual_change);
  554. if (err < 0)
  555. return err;
  556. ret = err;
  557. if (ret && dual_change) {
  558. err = __ethnl_set_coalesce(req_info, info, &dual_change);
  559. if (err < 0)
  560. return err;
  561. }
  562. return ret;
  563. }
  564. const struct ethnl_request_ops ethnl_coalesce_request_ops = {
  565. .request_cmd = ETHTOOL_MSG_COALESCE_GET,
  566. .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY,
  567. .hdr_attr = ETHTOOL_A_COALESCE_HEADER,
  568. .req_info_size = sizeof(struct coalesce_req_info),
  569. .reply_data_size = sizeof(struct coalesce_reply_data),
  570. .prepare_data = coalesce_prepare_data,
  571. .reply_size = coalesce_reply_size,
  572. .fill_reply = coalesce_fill_reply,
  573. .set_validate = ethnl_set_coalesce_validate,
  574. .set = ethnl_set_coalesce,
  575. .set_ntf_cmd = ETHTOOL_MSG_COALESCE_NTF,
  576. };