net_dim.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  2. /*
  3. * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved.
  4. */
  5. #include <linux/dim.h>
  6. #include <linux/rtnetlink.h>
  7. /*
  8. * Net DIM profiles:
  9. * There are different set of profiles for each CQ period mode.
  10. * There are different set of profiles for RX/TX CQs.
  11. * Each profile size must be of NET_DIM_PARAMS_NUM_PROFILES
  12. */
  13. #define NET_DIM_RX_EQE_PROFILES { \
  14. {.usec = 1, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
  15. {.usec = 8, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
  16. {.usec = 64, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
  17. {.usec = 128, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
  18. {.usec = 256, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,} \
  19. }
  20. #define NET_DIM_RX_CQE_PROFILES { \
  21. {.usec = 2, .pkts = 256,}, \
  22. {.usec = 8, .pkts = 128,}, \
  23. {.usec = 16, .pkts = 64,}, \
  24. {.usec = 32, .pkts = 64,}, \
  25. {.usec = 64, .pkts = 64,} \
  26. }
  27. #define NET_DIM_TX_EQE_PROFILES { \
  28. {.usec = 1, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}, \
  29. {.usec = 8, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}, \
  30. {.usec = 32, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}, \
  31. {.usec = 64, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,}, \
  32. {.usec = 128, .pkts = NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE,} \
  33. }
  34. #define NET_DIM_TX_CQE_PROFILES { \
  35. {.usec = 5, .pkts = 128,}, \
  36. {.usec = 8, .pkts = 64,}, \
  37. {.usec = 16, .pkts = 32,}, \
  38. {.usec = 32, .pkts = 32,}, \
  39. {.usec = 64, .pkts = 32,} \
  40. }
  41. static const struct dim_cq_moder
  42. rx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
  43. NET_DIM_RX_EQE_PROFILES,
  44. NET_DIM_RX_CQE_PROFILES,
  45. };
  46. static const struct dim_cq_moder
  47. tx_profile[DIM_CQ_PERIOD_NUM_MODES][NET_DIM_PARAMS_NUM_PROFILES] = {
  48. NET_DIM_TX_EQE_PROFILES,
  49. NET_DIM_TX_CQE_PROFILES,
  50. };
  51. struct dim_cq_moder
  52. net_dim_get_rx_moderation(u8 cq_period_mode, int ix)
  53. {
  54. struct dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix];
  55. cq_moder.cq_period_mode = cq_period_mode;
  56. return cq_moder;
  57. }
  58. EXPORT_SYMBOL(net_dim_get_rx_moderation);
  59. struct dim_cq_moder
  60. net_dim_get_def_rx_moderation(u8 cq_period_mode)
  61. {
  62. u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
  63. NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE;
  64. return net_dim_get_rx_moderation(cq_period_mode, profile_ix);
  65. }
  66. EXPORT_SYMBOL(net_dim_get_def_rx_moderation);
  67. struct dim_cq_moder
  68. net_dim_get_tx_moderation(u8 cq_period_mode, int ix)
  69. {
  70. struct dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix];
  71. cq_moder.cq_period_mode = cq_period_mode;
  72. return cq_moder;
  73. }
  74. EXPORT_SYMBOL(net_dim_get_tx_moderation);
  75. struct dim_cq_moder
  76. net_dim_get_def_tx_moderation(u8 cq_period_mode)
  77. {
  78. u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ?
  79. NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE;
  80. return net_dim_get_tx_moderation(cq_period_mode, profile_ix);
  81. }
  82. EXPORT_SYMBOL(net_dim_get_def_tx_moderation);
  83. int net_dim_init_irq_moder(struct net_device *dev, u8 profile_flags,
  84. u8 coal_flags, u8 rx_mode, u8 tx_mode,
  85. void (*rx_dim_work)(struct work_struct *work),
  86. void (*tx_dim_work)(struct work_struct *work))
  87. {
  88. struct dim_cq_moder *rxp = NULL, *txp;
  89. struct dim_irq_moder *moder;
  90. int len;
  91. dev->irq_moder = kzalloc_obj(*dev->irq_moder);
  92. if (!dev->irq_moder)
  93. return -ENOMEM;
  94. moder = dev->irq_moder;
  95. len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*moder->rx_profile);
  96. moder->coal_flags = coal_flags;
  97. moder->profile_flags = profile_flags;
  98. if (profile_flags & DIM_PROFILE_RX) {
  99. moder->rx_dim_work = rx_dim_work;
  100. moder->dim_rx_mode = rx_mode;
  101. rxp = kmemdup(rx_profile[rx_mode], len, GFP_KERNEL);
  102. if (!rxp)
  103. goto free_moder;
  104. rcu_assign_pointer(moder->rx_profile, rxp);
  105. }
  106. if (profile_flags & DIM_PROFILE_TX) {
  107. moder->tx_dim_work = tx_dim_work;
  108. moder->dim_tx_mode = tx_mode;
  109. txp = kmemdup(tx_profile[tx_mode], len, GFP_KERNEL);
  110. if (!txp)
  111. goto free_rxp;
  112. rcu_assign_pointer(moder->tx_profile, txp);
  113. }
  114. return 0;
  115. free_rxp:
  116. kfree(rxp);
  117. free_moder:
  118. kfree(moder);
  119. return -ENOMEM;
  120. }
  121. EXPORT_SYMBOL(net_dim_init_irq_moder);
  122. /* RTNL lock is held. */
  123. void net_dim_free_irq_moder(struct net_device *dev)
  124. {
  125. struct dim_cq_moder *rxp, *txp;
  126. if (!dev->irq_moder)
  127. return;
  128. rxp = rtnl_dereference(dev->irq_moder->rx_profile);
  129. txp = rtnl_dereference(dev->irq_moder->tx_profile);
  130. rcu_assign_pointer(dev->irq_moder->rx_profile, NULL);
  131. rcu_assign_pointer(dev->irq_moder->tx_profile, NULL);
  132. kfree_rcu(rxp, rcu);
  133. kfree_rcu(txp, rcu);
  134. kfree(dev->irq_moder);
  135. }
  136. EXPORT_SYMBOL(net_dim_free_irq_moder);
  137. void net_dim_setting(struct net_device *dev, struct dim *dim, bool is_tx)
  138. {
  139. struct dim_irq_moder *irq_moder = dev->irq_moder;
  140. if (!irq_moder)
  141. return;
  142. if (is_tx) {
  143. INIT_WORK(&dim->work, irq_moder->tx_dim_work);
  144. dim->mode = READ_ONCE(irq_moder->dim_tx_mode);
  145. return;
  146. }
  147. INIT_WORK(&dim->work, irq_moder->rx_dim_work);
  148. dim->mode = READ_ONCE(irq_moder->dim_rx_mode);
  149. }
  150. EXPORT_SYMBOL(net_dim_setting);
  151. void net_dim_work_cancel(struct dim *dim)
  152. {
  153. cancel_work_sync(&dim->work);
  154. }
  155. EXPORT_SYMBOL(net_dim_work_cancel);
  156. struct dim_cq_moder net_dim_get_rx_irq_moder(struct net_device *dev,
  157. struct dim *dim)
  158. {
  159. struct dim_cq_moder res, *profile;
  160. rcu_read_lock();
  161. profile = rcu_dereference(dev->irq_moder->rx_profile);
  162. res = profile[dim->profile_ix];
  163. rcu_read_unlock();
  164. res.cq_period_mode = dim->mode;
  165. return res;
  166. }
  167. EXPORT_SYMBOL(net_dim_get_rx_irq_moder);
  168. struct dim_cq_moder net_dim_get_tx_irq_moder(struct net_device *dev,
  169. struct dim *dim)
  170. {
  171. struct dim_cq_moder res, *profile;
  172. rcu_read_lock();
  173. profile = rcu_dereference(dev->irq_moder->tx_profile);
  174. res = profile[dim->profile_ix];
  175. rcu_read_unlock();
  176. res.cq_period_mode = dim->mode;
  177. return res;
  178. }
  179. EXPORT_SYMBOL(net_dim_get_tx_irq_moder);
  180. void net_dim_set_rx_mode(struct net_device *dev, u8 rx_mode)
  181. {
  182. WRITE_ONCE(dev->irq_moder->dim_rx_mode, rx_mode);
  183. }
  184. EXPORT_SYMBOL(net_dim_set_rx_mode);
  185. void net_dim_set_tx_mode(struct net_device *dev, u8 tx_mode)
  186. {
  187. WRITE_ONCE(dev->irq_moder->dim_tx_mode, tx_mode);
  188. }
  189. EXPORT_SYMBOL(net_dim_set_tx_mode);
  190. static int net_dim_step(struct dim *dim)
  191. {
  192. if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2))
  193. return DIM_TOO_TIRED;
  194. switch (dim->tune_state) {
  195. case DIM_PARKING_ON_TOP:
  196. case DIM_PARKING_TIRED:
  197. break;
  198. case DIM_GOING_RIGHT:
  199. if (dim->profile_ix == (NET_DIM_PARAMS_NUM_PROFILES - 1))
  200. return DIM_ON_EDGE;
  201. dim->profile_ix++;
  202. dim->steps_right++;
  203. break;
  204. case DIM_GOING_LEFT:
  205. if (dim->profile_ix == 0)
  206. return DIM_ON_EDGE;
  207. dim->profile_ix--;
  208. dim->steps_left++;
  209. break;
  210. }
  211. dim->tired++;
  212. return DIM_STEPPED;
  213. }
  214. static void net_dim_exit_parking(struct dim *dim)
  215. {
  216. dim->tune_state = dim->profile_ix ? DIM_GOING_LEFT : DIM_GOING_RIGHT;
  217. net_dim_step(dim);
  218. }
  219. static int net_dim_stats_compare(struct dim_stats *curr,
  220. struct dim_stats *prev)
  221. {
  222. if (!prev->bpms)
  223. return curr->bpms ? DIM_STATS_BETTER : DIM_STATS_SAME;
  224. if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms))
  225. return (curr->bpms > prev->bpms) ? DIM_STATS_BETTER :
  226. DIM_STATS_WORSE;
  227. if (!prev->ppms)
  228. return curr->ppms ? DIM_STATS_BETTER :
  229. DIM_STATS_SAME;
  230. if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms))
  231. return (curr->ppms > prev->ppms) ? DIM_STATS_BETTER :
  232. DIM_STATS_WORSE;
  233. if (!prev->epms)
  234. return DIM_STATS_SAME;
  235. if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms))
  236. return (curr->epms < prev->epms) ? DIM_STATS_BETTER :
  237. DIM_STATS_WORSE;
  238. return DIM_STATS_SAME;
  239. }
  240. static bool net_dim_decision(struct dim_stats *curr_stats, struct dim *dim)
  241. {
  242. int prev_state = dim->tune_state;
  243. int prev_ix = dim->profile_ix;
  244. int stats_res;
  245. int step_res;
  246. switch (dim->tune_state) {
  247. case DIM_PARKING_ON_TOP:
  248. stats_res = net_dim_stats_compare(curr_stats,
  249. &dim->prev_stats);
  250. if (stats_res != DIM_STATS_SAME)
  251. net_dim_exit_parking(dim);
  252. break;
  253. case DIM_PARKING_TIRED:
  254. dim->tired--;
  255. if (!dim->tired)
  256. net_dim_exit_parking(dim);
  257. break;
  258. case DIM_GOING_RIGHT:
  259. case DIM_GOING_LEFT:
  260. stats_res = net_dim_stats_compare(curr_stats,
  261. &dim->prev_stats);
  262. if (stats_res != DIM_STATS_BETTER)
  263. dim_turn(dim);
  264. if (dim_on_top(dim)) {
  265. dim_park_on_top(dim);
  266. break;
  267. }
  268. step_res = net_dim_step(dim);
  269. switch (step_res) {
  270. case DIM_ON_EDGE:
  271. dim_park_on_top(dim);
  272. break;
  273. case DIM_TOO_TIRED:
  274. dim_park_tired(dim);
  275. break;
  276. }
  277. break;
  278. }
  279. if (prev_state != DIM_PARKING_ON_TOP ||
  280. dim->tune_state != DIM_PARKING_ON_TOP)
  281. dim->prev_stats = *curr_stats;
  282. return dim->profile_ix != prev_ix;
  283. }
  284. void net_dim(struct dim *dim, const struct dim_sample *end_sample)
  285. {
  286. struct dim_stats curr_stats;
  287. u16 nevents;
  288. switch (dim->state) {
  289. case DIM_MEASURE_IN_PROGRESS:
  290. nevents = BIT_GAP(BITS_PER_TYPE(u16),
  291. end_sample->event_ctr,
  292. dim->start_sample.event_ctr);
  293. if (nevents < DIM_NEVENTS)
  294. break;
  295. if (!dim_calc_stats(&dim->start_sample, end_sample, &curr_stats))
  296. break;
  297. if (net_dim_decision(&curr_stats, dim)) {
  298. dim->state = DIM_APPLY_NEW_PROFILE;
  299. schedule_work(&dim->work);
  300. break;
  301. }
  302. fallthrough;
  303. case DIM_START_MEASURE:
  304. dim_update_sample(end_sample->event_ctr, end_sample->pkt_ctr,
  305. end_sample->byte_ctr, &dim->start_sample);
  306. dim->state = DIM_MEASURE_IN_PROGRESS;
  307. break;
  308. case DIM_APPLY_NEW_PROFILE:
  309. break;
  310. }
  311. }
  312. EXPORT_SYMBOL(net_dim);