mtier.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * memory tiering: migrate cold pages in node 0 and hot pages in node 1 to node
  4. * 1 and node 0, respectively. Adjust the hotness/coldness threshold aiming
  5. * resulting 99.6 % node 0 utilization ratio.
  6. */
  7. #define pr_fmt(fmt) "damon_sample_mtier: " fmt
  8. #include <linux/damon.h>
  9. #include <linux/init.h>
  10. #include <linux/kernel.h>
  11. #include <linux/module.h>
  12. #ifdef MODULE_PARAM_PREFIX
  13. #undef MODULE_PARAM_PREFIX
  14. #endif
  15. #define MODULE_PARAM_PREFIX "damon_sample_mtier."
  16. static unsigned long node0_start_addr __read_mostly;
  17. module_param(node0_start_addr, ulong, 0600);
  18. static unsigned long node0_end_addr __read_mostly;
  19. module_param(node0_end_addr, ulong, 0600);
  20. static unsigned long node1_start_addr __read_mostly;
  21. module_param(node1_start_addr, ulong, 0600);
  22. static unsigned long node1_end_addr __read_mostly;
  23. module_param(node1_end_addr, ulong, 0600);
  24. static unsigned long node0_mem_used_bp __read_mostly = 9970;
  25. module_param(node0_mem_used_bp, ulong, 0600);
  26. static unsigned long node0_mem_free_bp __read_mostly = 50;
  27. module_param(node0_mem_free_bp, ulong, 0600);
  28. static int damon_sample_mtier_enable_store(
  29. const char *val, const struct kernel_param *kp);
  30. static const struct kernel_param_ops enabled_param_ops = {
  31. .set = damon_sample_mtier_enable_store,
  32. .get = param_get_bool,
  33. };
  34. static bool enabled __read_mostly;
  35. module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
  36. MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_MTIER");
  37. static bool detect_node_addresses __read_mostly;
  38. module_param(detect_node_addresses, bool, 0600);
  39. static struct damon_ctx *ctxs[2];
  40. struct region_range {
  41. phys_addr_t start;
  42. phys_addr_t end;
  43. };
  44. static int nid_to_phys(int target_node, struct region_range *range)
  45. {
  46. if (!node_online(target_node)) {
  47. pr_err("NUMA node %d is not online\n", target_node);
  48. return -EINVAL;
  49. }
  50. range->start = PFN_PHYS(node_start_pfn(target_node));
  51. range->end = PFN_PHYS(node_end_pfn(target_node));
  52. return 0;
  53. }
  54. static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
  55. {
  56. struct damon_ctx *ctx;
  57. struct damon_attrs attrs;
  58. struct damon_target *target;
  59. struct damon_region *region;
  60. struct damos *scheme;
  61. struct damos_quota_goal *quota_goal;
  62. struct damos_filter *filter;
  63. struct region_range addr;
  64. int ret;
  65. ctx = damon_new_ctx();
  66. if (!ctx)
  67. return NULL;
  68. attrs = (struct damon_attrs) {
  69. .sample_interval = 5 * USEC_PER_MSEC,
  70. .aggr_interval = 100 * USEC_PER_MSEC,
  71. .ops_update_interval = 60 * USEC_PER_MSEC * MSEC_PER_SEC,
  72. .min_nr_regions = 10,
  73. .max_nr_regions = 1000,
  74. };
  75. /*
  76. * auto-tune sampling and aggregation interval aiming 4% DAMON-observed
  77. * accesses ratio, keeping sampling interval in [5ms, 10s] range.
  78. */
  79. attrs.intervals_goal = (struct damon_intervals_goal) {
  80. .access_bp = 400, .aggrs = 3,
  81. .min_sample_us = 5000, .max_sample_us = 10000000,
  82. };
  83. if (damon_set_attrs(ctx, &attrs))
  84. goto free_out;
  85. if (damon_select_ops(ctx, DAMON_OPS_PADDR))
  86. goto free_out;
  87. target = damon_new_target();
  88. if (!target)
  89. goto free_out;
  90. damon_add_target(ctx, target);
  91. if (detect_node_addresses) {
  92. ret = promote ? nid_to_phys(1, &addr) : nid_to_phys(0, &addr);
  93. if (ret)
  94. goto free_out;
  95. } else {
  96. addr.start = promote ? node1_start_addr : node0_start_addr;
  97. addr.end = promote ? node1_end_addr : node0_end_addr;
  98. }
  99. region = damon_new_region(addr.start, addr.end);
  100. if (!region)
  101. goto free_out;
  102. damon_add_region(region, target);
  103. scheme = damon_new_scheme(
  104. /* access pattern */
  105. &(struct damos_access_pattern) {
  106. .min_sz_region = PAGE_SIZE,
  107. .max_sz_region = ULONG_MAX,
  108. .min_nr_accesses = promote ? 1 : 0,
  109. .max_nr_accesses = promote ? UINT_MAX : 0,
  110. .min_age_region = 0,
  111. .max_age_region = UINT_MAX},
  112. /* action */
  113. promote ? DAMOS_MIGRATE_HOT : DAMOS_MIGRATE_COLD,
  114. 1000000, /* apply interval (1s) */
  115. &(struct damos_quota){
  116. /* 200 MiB per sec by most */
  117. .reset_interval = 1000,
  118. .sz = 200 * 1024 * 1024,
  119. /* ignore size of region when prioritizing */
  120. .weight_sz = 0,
  121. .weight_nr_accesses = 100,
  122. .weight_age = 100,
  123. },
  124. &(struct damos_watermarks){},
  125. promote ? 0 : 1); /* migrate target node id */
  126. if (!scheme)
  127. goto free_out;
  128. damon_set_schemes(ctx, &scheme, 1);
  129. quota_goal = damos_new_quota_goal(
  130. promote ? DAMOS_QUOTA_NODE_MEM_USED_BP :
  131. DAMOS_QUOTA_NODE_MEM_FREE_BP,
  132. promote ? node0_mem_used_bp : node0_mem_free_bp);
  133. if (!quota_goal)
  134. goto free_out;
  135. quota_goal->nid = 0;
  136. damos_add_quota_goal(&scheme->quota, quota_goal);
  137. filter = damos_new_filter(DAMOS_FILTER_TYPE_YOUNG, true, promote);
  138. if (!filter)
  139. goto free_out;
  140. damos_add_filter(scheme, filter);
  141. return ctx;
  142. free_out:
  143. damon_destroy_ctx(ctx);
  144. return NULL;
  145. }
  146. static int damon_sample_mtier_start(void)
  147. {
  148. struct damon_ctx *ctx;
  149. ctx = damon_sample_mtier_build_ctx(true);
  150. if (!ctx)
  151. return -ENOMEM;
  152. ctxs[0] = ctx;
  153. ctx = damon_sample_mtier_build_ctx(false);
  154. if (!ctx) {
  155. damon_destroy_ctx(ctxs[0]);
  156. return -ENOMEM;
  157. }
  158. ctxs[1] = ctx;
  159. return damon_start(ctxs, 2, true);
  160. }
  161. static void damon_sample_mtier_stop(void)
  162. {
  163. damon_stop(ctxs, 2);
  164. damon_destroy_ctx(ctxs[0]);
  165. damon_destroy_ctx(ctxs[1]);
  166. }
  167. static int damon_sample_mtier_enable_store(
  168. const char *val, const struct kernel_param *kp)
  169. {
  170. bool is_enabled = enabled;
  171. int err;
  172. err = kstrtobool(val, &enabled);
  173. if (err)
  174. return err;
  175. if (enabled == is_enabled)
  176. return 0;
  177. if (!damon_initialized())
  178. return 0;
  179. if (enabled) {
  180. err = damon_sample_mtier_start();
  181. if (err)
  182. enabled = false;
  183. return err;
  184. }
  185. damon_sample_mtier_stop();
  186. return 0;
  187. }
  188. static int __init damon_sample_mtier_init(void)
  189. {
  190. int err = 0;
  191. if (!damon_initialized()) {
  192. if (enabled)
  193. enabled = false;
  194. return -ENOMEM;
  195. }
  196. if (enabled) {
  197. err = damon_sample_mtier_start();
  198. if (err)
  199. enabled = false;
  200. }
  201. return 0;
  202. }
  203. module_init(damon_sample_mtier_init);